How do I test the default view when using T4MVC with MvcContrib.TestHelper AssertViewRendered - asp.net-mvc-3

I am using T4MVC in my ASP.NET MVC 3 project. I have the following basic test:
[TestMethod]
public void IndexReturnsIndexView()
{
var controller = new HomeController();
var result = controller.Index();
result.AssertViewRendered().ForView(MVC.Home.Views.Index);
}
The test fails if the controller method returns the default View:
public virtual ActionResult Index()
{
return View();
}
The error given is:
MvcContrib.TestHelper.ActionResultAssertionException: Expected view name '~/Views/Home/Index.cshtml', actual was ''
But the test passes if I override the View to specify which viewName to return:
public virtual ActionResult Index()
{
return View(MVC.Home.Views.Index);
}
I tried using the following assertion, but still not luck:
result.AssertViewRendered().ForView(MVC.Home.Index().GetT4MVCResult().Action);
The following error is raised:
MvcContrib.TestHelper.ActionResultAssertionException: Expected view name 'Index', actual was ''
I then realized that I had misread the assertion failure, so I changed the test to this:
result.AssertViewRendered().ForView(String.Empty);
The test passes, but the test itself seems to be useless.
Preferably I don't want to have to specify all views by name, so how do I test this? To clarify, I am using the MvcContrib.Mvc3.TestHelper-ci 3.0.96.0, which I installed today from NuGet.
UPDATE
This isn't an answer to the question, but I have started doing the following instead, which provides more value as a test case:
using (var controller = new FeatureController(mockGateway))
{
// Act
var result = controller.Index();
var model = result.ViewData.Model as MyModel;
// Assert
Assert.IsNotNull(model, "Model is null or wrong type");
result.AssertViewRendered().WithViewData<MyModel>();
// Alternative Assert for model data
Assert.IsTrue(model.Items.Count > 0);
}

I left this open for a considerable amount of time, so that anyone else could answer if they chose to. I'll now answer this myself.
The following checks to make sure that the view returned contains the expected model, and more appropriately data within that model. This is a much better unit test for the controller in question.
using (var controller = new FeatureController(mockGateway))
{
// Act
var result = controller.Index();
var model = result.ViewData.Model as MyModel;
// Assert
Assert.IsNotNull(model, "Model is null or wrong type");
result.AssertViewRendered().WithViewData<MyModel>();
// Alternative Assert for model data
Assert.IsTrue(model.Items.Count > 0);
}

AssertViewRendered().ForView() tests against the view name that you explicitly pass to View() is your controller action. If, as you're doing, you don't specify a view name in your action, then .ForView() will be testing against an empty string, as you've seen.
If you called View("Index"); in your action, you could call .ForView("Index") in your test.
I would think that this assertion would be most useful if your action could return different possible views.

Related

Mocking Request.QueryString for Unit Tests and asserting against views

I have the following controller:
public class ResetController : Controller
{
//
// GET: /Reset/
private Models.ResetModel rm = new Models.ResetModel();
public ActionResult Index()
{
//Check that this has a query string that is containing in the database
//and has been done in the last 30 mins.
string qString = Request.QueryString["v"].ToString();
//if this is a good querystring
if (rm.CheckQString(qString))
return View();
else
return View("Index", "Home");
}
I now need to create a unit test to ensure that if the Request.QueryString value is found in the database then the appropriate view is returned but I am unable to do so. Here is my attempt at a test to check this:
[TestMethod()]
public void IndexTest()
{
ResetController target = new ResetController();
var request = new Mock<HttpRequestBase>();
request.SetupGet(r => r.QueryString).Returns(HttpUtility.ParseQueryString("?v=0ocIqhOQkrBaCXRO96E4B5HcOCYgMfJYOpRdNU/yIEUmH2szuXXKU51Td6NzRxlk"));
var result = target.Index() as ActionResult;
Assert.IsNotNull(result);
}
Can someone please help me with suggestions to ensure that this controller is fully tested?
Thanks
This is a late answer, but in the event that someone comes along this post in the future... Refer to this post how would I mock a querystring
The goal is to isolate the test so that it does not depend on the QueryString result from the database, but rather a provided value. To do this in Moq use the SetupGet method after creating a Mock Context. Hope this helps someone!
I would suggest you pass the model as a dependency to the controller. Then you can mock it as well in the unit test to isolate your controller logic from the model's CheckQString implementation logic.
I'm not sure though if I understand your problem correctly.
The good case might then look like this. Of course you would need to check if the correct view was returned.
[TestMethod()]
public void IndexTest()
{
const string query = "some query";
Models.ResetModel rm = new Mock<Models.ResetModel>();
rm.Setup(m => m.CheckQString(query)).Returns(true);
ResetController target = new ResetController(rm.Object);
var request = new Mock<HttpRequestBase>();
request.SetupGet(r => r.QueryString).Returns(HttpUtility.ParseQueryString("?v=" + query));
var result = target.Index() as ActionResult;
Assert.IsNotNull(result);
}

How to update knockout model in mvc3 app

I've been playing with MVC3 with KnockoutJs for a few weeks and I've been wondering about something
say I have an mvc action which returns a simple list
public ActionResult AddFoos()
{
List<Foo> funds = new List<Foo>();
.. etc
return Json(funds.ToList(), JsonRequestBehavior.AllowGet);
}
which is then passed into the view model
var viewModel = {
fooChocies: ko.mapping.fromJS([]),
fooOptions: ko.mapping.fromJS([]),
loadInitialData: function () {
ko.mapping.fromJS(serverData, dataMappingOptions, viewModel.fooOptions);
},
};
In my type Foo I also have properties that show or hide ui elements
var Foo = function (data, preselect) {
var self = this;
self.id = ko.observable(data.id);
self.Name = ko.observable(data.Name);
self.number = ko.observable('');
self.showProducts = ko.observable(false); <---
self.displayBigPanel = ko.observable(false); <---
}
My approach so far as been to dynamically create elements of the form
which passes through the ModelBinder and creates a List< Foo > as a parameter for controller action.
Finally the question...
When the user navigates back to this page I need to restore the UI with the fooChoices the user made.
It seems I have 2 choices with rebuilding the user selections (both via extension methods)
Use raw json as seen by
ko.toJSON(viewModel.fooChoices))
which in addition to basic model properties also provides info on hiding and displaying UI elements,
sb.Append("viewModel.fooCghoices= ko.mapping.fromJS(" + json + ");");
sb.Append("ko.applyBindings(viewModel);");
return new HtmlString(sb.ToString());
thus sending client ui info to the server and back
or
Manipulate the ViewModel directly in effect simulating the user actions
sb.Append("viewModel.fooChoices.push(new Foo(1509));");
sb.Append("viewModel.fooChoices()[0].selectedSubFoo = new Foo(273);");
sb.Append("viewModel.fooChoices()[0].showProducts(true);");
In either case it feels a bit off and that a better pattern is out there. Would like to know if one way is better than the other or none of the above.
Many Thanks
Presently, your controller method returns a list of Foo. Consider creating a more complex object that holds both your Foos and your choices.
public class FooViewModel
{
public List<Foo> Foos { get; set; };
public UserChoices { get; set; }
}
Change your controller method so that it returns FooViewModel. This means user choices will be returned along with any Foos you are interested in.
public ActionResult AddFoos()
{
// Are there any choices stored in session?
// Use those first, otherwise create a new UserChoices object
UserChoices choices =
Session["User.Choices"] as UserChoices ?? new UserChoices();
List<Foo> funds = new List<Foo>();
.. etc
FooViewModel vm = new FooViewModel() { Foos = funds; UserChoices = choices };
// Return the whole object, containing Choices and Foos
return Json(vm, JsonRequestBehavior.AllowGet);
}
Also, consider some kind of action filter to allow you to pass complete objects easily. ObjectFilter is a good approach. It allows you to pass complex object structures easily without having to rely on specific markup.
http://www.c-sharpcorner.com/blogs/863/passing-json-into-an-asp-net-mvc-controller.aspx
ObjectFilter above a controller method. Pretty simple, just declaring that the controller should attempt to treat any incoming parameter called fooStuff as type FooViewModel.
[HttpPost,
ObjectFilter(Param = "fooStuff", RootType = typeof(FooViewModel)),
UnitOfWork]
public JsonResult ProcessFoos(FooViewModel fooStuff) {
By defining a corresponding JavaScript view model, you can just convert the whole thing to a json string and pass it to the controller method fully populated.
So, example of corresponding js vm would be:-
var fooViewModel = function(data) {
var self = this;
self.Foos = ko.observableArray(data.Foos);
self.UserChoices = ko.observable(data.UserChoices);
// Don't worry about properties or methods that don't exist
// on the C# end of things. They'll just be ignored.
self.usefulJSOnlyMethod = function() {
// behaviour
};
}
var userChoice = function(data) {
var self = this;
self.DinnerId = ko.observable(data.DinnerId);
}
Typical call to a controller method decorated by ObjectFilter would be something like this ( assuming self is a fooViewModel ):-
var queryData = ko.mapping.toJSON(self);
$.ajax(
//...
data: queryData,
Any matching ( same name, same type case-sensitive ) property from the js vm will automatically end up in the fooStuff parameter of your controller method. Time to save those choices:-
Also note that I'm persisting user choices in the session here. This'll allow them to be picked up by any other controller method which may need them ( example in AddFoos above ).
[HttpPost,
ObjectFilter(Param = "fooStuff", RootType = typeof(FooViewModel)),
UnitOfWork]
public JsonResult ProcessFoos(FooViewModel fooStuff)
{
// hey! I have a fully mapped FooViewModel right here!
// ( _fooServices.ProcessFoos will return updated version of viewmodel )
FooViewModel vm = _fooServices.ProcessFoos(fooStuff);
// What about those choices?
// Put them in the session at this point in case anyone else comes asking
// after them.
Session["User.Choices"] = vm.UserChoices;
return Json(vm);
}
Because we've:-
Defined a better C# view model
Defined a corresponding JS view model
Including UserChoices as part of that view model
....restoring the choice is simple at this point. Reference the part of the view model that contains the user's selected choice.
<select id="dinnerChoice"
data-bind="value: UserChoices.DinnerId"
>
</select>

How to compose and post a JSON object from jQuery to an MVC3 action method?

I have the following JavaScript code that gets the Id property (Guid) from every user row in a Kendo UI grid. Now I am wondering how best to compose these Id's and the owner roleId into a JSON object that I can pass to an MVC3 action method. Versus my silly string concat.
$("#command-add-selected").click(function () {
var json = "roleId: '51FC554E-353C-4D55-BE52-1B4BF9D2F17F', users: [";
var avail = $("#availableUsersGrid").data().kendoGrid._data;
for (var i = 0; i < avail.length; i++) {
json += "{ Id: '" + avail[i].Id + "'},";
}
json = json.slice(0, -1);
json += "]";
alert(json);
return false;
});
The action method can be GET or POST and need not return any value (this is another puzzle here, no returned view). All it does is domain updates that are fetched by other ajax code subsequent to the above code.
How can I pass the above type JSON to an action method essentially of void return type?
EDIT: This question answered the minor part of my question nicely, with how to dynamically add items to an array with push.
1.first of all u dont need to create the full json ur self use JSON.Stringify() method to change the javascript object to JSON string.
2.after u have created the JSON string u can GET or POST it to any normal method in any MVC Controller of visibility public.
even if the signature of the action method is like public ActionResult MehodName(string jsonString) u can always return null.
3. u can use built in JavaScriptSerializer class in System.Web.Script.Serialization namespace to deserialize the json string u recieve in the action to create an object with the same propertiese
Edit:-
make a javascript array names users then inside the for loop use .push() function of javascript to insert the objects like this
var users = [];
for(something)
{
var user = {"Id":"YOUR ID VALUE"};
users.push(user)
}
var objectToSerialize = {"roleId":"YOUR ROLE GUID","Users":users};
var jsonString = JSON.stringify(objectToSerialize);
Edit 2:-
so going by your previous comments u dont want that u need to deseralize the whole JSON object. going by your object architecture even if ur action method has a signature like this
public ActionResult GetUsersByRole(Users users)
{
//some code
}
and Users class like this one
class Users
{
public string RoleId{get; set;}
public User[]{get; set;}
}
and User class like this
class User
{
string Id{get; set;}
}
it would automatically bind property with your complex users object
In conjunction with Parv Sharma's solution:
function User(id) { this.Id=id; }
$("#command-add-selected").click(function () {
var avail = $("#availableUsersGrid").data().kendoGrid._data;
var userArray = array();
for (var i = 0; i < avail.length; i++) {
userArray.push(new User(avail[i].Id));
}
var obj = {roleId:'51FC554E-353C-4D55-BE52-1B4BF9D2F17F',users:userArray};
alert(JSON.stringify(obj));
return false;
});
Should just be able to use Url.Action("NameofAction","nameofcontroller", json);
You may have to add an AcceptVerbs attribute to the action method as well, depending on if you want it to be a GET or a POST.
As far as the building part goes, I would suggest not using strings at all. Jsons are objects, not strings, so I would go ahead and build a "users" object with your foreach loop and then throw that object into your json return object.
edit: forgot to mention stringify. Yeah. Use that.

How to pass class via RedirectToAction

I have the following code:
public ActionResult Index()
{
AdminPreRegUploadModel model = new AdminPreRegUploadModel()
{
SuccessCount = successAddedCount,
FailureCount = failedAddedCount,
AddedFailure = addedFailure,
AddedSuccess = addedSuccess
};
return RedirectToAction("PreRegExceUpload", new { model = model });
}
public ActionResult PreRegExceUpload(AdminPreRegUploadModel model)
{
return View(model);
}
but model is null when I breakpoint on PreRegExcelUpload. Why?
Instead of using the Session object in Evgeny Levin's answer I would suggest to use TempData. See http://rachelappel.com/when-to-use-viewbag-viewdata-or-tempdata-in-asp.net-mvc-3-applications about TempData.
You could also fix this by calling return PreRegExceUpload(model); instead of return RedirectToAction(..) in you Index function.
TempData is just a "smart" wrapper for the Session, under the hood it still acts the same way.
Since it's only 4 fields, i would pass them via querystring.
Always try and avoid session/tempdata where possible, for which in this scenario it certainly is.
Are you sure that's your full code? As it doesn't make sense.
If your POST'ing some data and saving it to the database (for example), usually you redirect to another action passing the unique identifier (which is usually generated after the save), fetch it back from the DB and return the view.
That is much better practice.
If you explain your scenario a bit more, and show the proper code your using, i can help further.
Use session to pass model to method:
public ActionResult Index()
{
AdminPreRegUploadModel model = new AdminPreRegUploadModel()
{
SuccessCount = successAddedCount,
FailureCount = failedAddedCount,
AddedFailure = addedFailure,
AddedSuccess = addedSuccess
};
Session["someKey"] = model;
return RedirectToAction("PreRegExceUpload");
}
public ActionResult PreRegExceUpload()
{
var model = (AdminPreRegUploadModel) Session["someKey"];
Session["someKey"] = null;
return View(model);
}
Method RedirectToAction() can't take non primitive types as parameters, because url parameters is string.

TryUpdateModel causing error from unit test cases (Asp.net mvc)

I have on post action in controller. Code is as given below
[HttpPost]
public ActionResult Create(Int64 id, FormCollection collection)
{
var data = Helper.CreateEmptyApplicationsModel();
if (TryUpdateModel(data))
{
// TODO: Save data
return RedirectToAction("Edit", "Applications", new { area = "Applications", id = id });
}
else
{
// TODO: update of the model has failed, look at the error and pass back to the view
if (!ModelState.IsValid)
{
if (id != 0) Helper.ShowLeftColumn(data, id);
return View("Create", data);
}
}
return RedirectToAction("Details", "Info", new { area = "Deals", InfoId= id });
}
I wrote test case for this as below
[TestMethod]
public void CreateTest_for_post_data()
{
var collection = GetApplicantDataOnly();
_controller.ValueProvider = collection.ToValueProvider();
var actual = _controller.Create(0, collection);
Assert.IsInstanceOfType(actual, typeof(RedirectToRouteResult));
}
When I debug this single test case, test case passed because condition
if (TryUpdateModel(data)) return true and its goes to if condition.
But when I debug test cases from entire solution this test case failed because it goes to else condition of " if (TryUpdateModel(data))".
I dont know why..
Please help...
Thanks
I've experienced a similar problem which will solve your issue provided you don't need to use a FormCollection.
I haven't used TryUpdateModel ever since the day I learned of the Auto-Binding feature. Auto-binding, in a nutshell pretty much does the work TryUpdateModel would do for you, that is, it'll set a model object according to the values found in the FormCollection as well as attempting to validate the model. And it does this automatically. All you'll have to do is place a parameter in the ActionMethod and it will automatically have it's properties filled with the values found in the FormCollection. Your Action signature will then turn into this:
public ActionResult Create(Int64 id, SomeModel data)
Now you don't need to call TryUpdateModel at all. You still need to check if the ModelState is valid to decide whether or not to redirect or return a view.
[HttpPost]
public ActionResult Create(Int64 id, SomeModel data)
{
if (ModelState.IsValid)
{
// TODO: Save data
return RedirectToAction("Edit", "Applications", new { area = "Applications", id = id });
}
else
{
if (id != 0) Helper.ShowLeftColumn(data, id);
return View("Create", data);
}
}
This won't throw an exception in your unit tests so that's one problem solved. However, there's another problem now. If you run your application using the above code, it'll work just fine. You model will be validated upon entering the Action and the correct ActionResult, either a redirect or a view, will be returned. However, when you try to unit test both paths, you'll find the model will always return the redirect, even when the model is invalid.
The problem is that when unit testing, the model isn't being validated at all. And since the ModelState is by default valid, ModelState.IsValid will always return true in your unit tests and thus will always return a redirect even when the model is invalid.
The solution: call TryValidateModel instead of ModelState.IsValid. This will force your unit test to validate the model. One problem with this approach is that means the model will be validated once in your unit tests and twice in your application. Which means any errors discovered will be recorded twice in your application. Which means if you use the ValidationSummary helper method in your view, your gonna see some duplicated messages listed.
If this is too much to bear, you can clear the ModelState before calling TryValidateModel. There are some problems with doing so because your gonna lose some useful data, such as the attempted value if you clear the ModelState so you could instead just clear the errors recorded in the ModelState. You can do so by digging deep into the ModelState and clearing every error stored in every item like so:
protected void ClearModelStateErrors()
{
foreach (var modelState in ModelState.Values)
modelState.Errors.Clear();
}
I've placed the code in a method so it can be reused by all Actions. I also added the protected keyword to hint that this might be a useful method to place in a BaseController that all your controllers derive from so that they all have access to this method.
Final Solution:
[HttpPost]
public ActionResult Create(Int64 id, SomeModel data)
{
ClearModelStateErrors();
if (ModelState.IsValid)
{
// TODO: Save data
return RedirectToAction("Edit", "Applications", new { area = "Applications", id = id });
}
else
{
if (id != 0) Helper.ShowLeftColumn(data, id);
return View("Create", data);
}
}
NOTE: I realize I didn't shed any light on the root issue. That's because I don't completely understand the root issue. If you notice the failing unit test, it fails because a ArgumentNullException was thrown because the ControllerContext is null and it is passed to a method which throws an exception if the ControllerContext is null. (Curse the MVC team with their damned defensive programming).
If you attempt to mock the ControllerContext, you'll still get an exception, this time a NullReferenceException. Funnily enough, the stack trace for the exception shows that both exceptions occur at the same method, or should I say constructor, located at System.Web.Mvc.ChildActionValueProvider. I don't have a copy of the source code handy so I have no idea what is causing the exception and I've yet to find a better solution than the one I offered above. I personally don't like my solution because I'm changing the way I code my application for the benefit of my unit tests but there doesn't seem to be a better alternative. I bet the real solution will involve mocking some other object but I just don't know what.
Also, before anyone gets any smart ideas, mocking the ValueProvider is not a solution. It'll stop exceptions but your unit tests won't be validating your model and your ModelState will always report that the model is valid even when it isn't.
You might want to clean up your code a bit:
[HttpPost]
public ActionResult Create(int id, FormCollection collection)
{
var data = Helper.CreateEmptyApplicationsModel();
if (!ModelState.IsValid)
{
if (id != 0)
{
Helper.ShowLeftColumn(data, id);
}
return View("Create", data);
}
if (TryUpdateModel(data))
{
return RedirectToAction("Edit", "Applications", new { area = "Applications", id = id });
}
return RedirectToAction("Details", "Info", new { area = "Deals", InfoId= id });
}
Don't use Int64, just use int.
As for your failing test, I would expect your test to fail all the time since TryUpdateModel will return false. As your running the code from a unit test, the controller context for a controller is not available, thus TryUpdateModel will fail.
You need to somehow fake/mock TryUpdateModel so that it does not actually run properly. Instead you "fake" it to return true. Here's some links to help:
How do I Unit Test Actions without Mocking that use UpdateModel?
The above SO answer shows an example using RhinoMocks which is a free mocking framework.
Or this:
http://www.codecapers.com/post/ASPNET-MVC-Unit-Testing-UpdateModel-and-TryUpdateModel.aspx
Debug your tests and check the modelstate errors collection, all the errors that the tryupdatemodel encountered should be there.

Resources