Unit testing a controller in ASP.NET MVC 2 with RedirectToAction - asp.net-mvc

I have a controller that implements a simple Add operation on an entity and redirects to the Details page:
[HttpPost]
public ActionResult Add(Thing thing)
{
// ... do validation, db stuff ...
return this.RedirectToAction<c => c.Details(thing.Id));
}
This works great (using the RedirectToAction from the MvcContrib assembly).
When I'm unit testing this method I want to access the ViewData that is returned from the Details action (so I can get the newly inserted thing's primary key and prove it is now in the database).
The test has:
var result = controller.Add(thing);
But result here is of type: System.Web.Mvc.RedirectToRouteResult (which is a System.Web.Mvc.ActionResult). It doesn't hasn't yet executed the Details method.
I've tried calling ExecuteResult on the returned object passing in a mocked up ControllerContext but the framework wasn't happy with the lack of detail in the mocked object.
I could try filling in the details, etc, etc but then my test code is way longer than the code I'm testing and I feel I need unit tests for the unit tests!
Am I missing something in the testing philosophy? How do I test this action when I can't get at its returned state?

I am using MVC2 RC2 at the moment and the answer from rmacfie didn't quite work for me but did get me on the right track.
Rightly or wrongly I managed to do this in my test instead:
var actionResult = (RedirectToRouteResult)logonController.ForgotUsername(model);
actionResult.RouteValues["action"].should_be_equal_to("Index");
actionResult.RouteValues["controller"].should_be_equal_to("Logon");
Not sure if this will help someone but might save you 10 minutes.

There is MVC Contrib TestHelper that are fantastic for testing most of the ActionResult
You can get it here:
http://mvccontrib.codeplex.com/wikipage?title=TestHelper
Here is an example of the syntax:
var controller = new TestController();
controller.Add(thing)
.AssertActionRedirect()
.ToAction<TestController>(x => x.Index());
To test if the data has been persisted successfully, you should maybe ask your database directly, I don't know if you're using an ORM or something, but you should do something to get the last insterted item in your database, then compare with the value you provided to your Add ActionResult and see if this is ok.
I don't think that testing your Details ActionResult to see if your data is persisted is the right approach. That would not be an unit test, more a functional test.
But you should also unit test your Details method to make sure that your viewdata object is filled with the right data coming from your database.

You seem to be doing way too much for a unit test. The validation and data access would typically be done by services that you call from the controller action. You mock those services and only test that they were called properly.
Something like this (using approximate syntax for Rhino.Mocks & NUnit):
[Test]
public void Add_SavesThingToDB()
{
var dbMock = MockRepository.GenerateMock<DBService>();
dbMock.Expect(x => x.Save(thing)).Repeat.Once();
var controller = new MyController(dbMock);
controller.Add(new Thing());
dbMock.VerifyAllExpectations();
}
[Test]
public void Add_RedirectsAfterSave()
{
var dbMock = MockRepository.GenerateMock<DBService>();
var controller = new MyController(dbMock);
var result = (RedirectToRouteResult)controller.Add(new Thing());
Assert.That(result.Url, Is.EqualTo("/mynew/url"));
}

I have a static helper method that tests redirection.
public static class UnitTestHelpers
{
public static void ShouldEqual<T>(this T actualValue, T expectedValue)
{
Assert.AreEqual(expectedValue, actualValue);
}
public static void ShouldBeRedirectionTo(this ActionResult actionResult, object expectedRouteValues)
{
RouteValueDictionary actualValues = ((RedirectToRouteResult)actionResult).RouteValues;
var expectedValues = new RouteValueDictionary(expectedRouteValues);
foreach (string key in expectedValues.Keys)
{
Assert.AreEqual(expectedValues[key], actualValues[key]);
}
}
}
Then creating a redirection test is very easy.
[Test]
public void ResirectionTest()
{
var result = controller.Action();
result.ShouldBeRedirectionTo(
new
{
controller = "ControllerName",
action = "Index"
}
);
}

Related

How to mock Request.IsAjaxRequest() to true using FakeItEasy?

Following is the code snippet for which I want to write unit tests:
[HttpGet]
public ActionResult Edit(string id)
{
if (Request.IsAjaxRequest())
{
EditModel model = new EditModel();
.....
}
return View();
}
I want to write unit tests for this action where I can fake the result of Request.IsAjaxRequest() to true so that I can write tests for rest of the code of the action.
I have tried following but it doesn't work. _request.Headers is always empty, and Request.IsAjaxRequest() is always returning false:
[Fact]
public void Get_Edit_AjaxRequest_ExpectedActionCalled()
{
HttpRequestBase _request = A.Fake<HttpRequestBase>();
_request.Headers.Add("X-Requested-With", "XMLHttpRequest");
_controller.ControllerContext = A.Fake<ControllerContext>();
_controller.ControllerContext.HttpContext = _request;
A.CallTo(() => _controller.Request).Returns(_request);
var result = _controller.Edit(1) as RedirectToRouteResult;
}
I always get Request.IsAjaxRequest() as false. Any help on this much appreciated. Thanks
I managed to muddle past the compilation errors and use some information from Chapter 10 of FakeItEasy Succinctly, which is all about ASP.NET MVC.
Generally speaking, the ASP.NET MVC classes are not designed in a way to make them easily fakeable, but I have a test setup that causes IsAjaxRequest to return true. The two main hurdles were getting the controller to use the request object and to make sure that the request object was returning the headers we wanted.
The first part was not hard, but the second required us to have the request object use a concrete NameValueCollection. The faked one that it had been providing by default was not useful, because the right properties weren't virtual. Fortunately, using a real NameValueCollection did the trick.
Try this:
[Fact]
public void Get_Edit_AjaxRequest_ExpectedActionCalled_Blair()
{
HttpRequestBase _request = A.Fake<HttpRequestBase>();
// NameValueCollection is effectively unfakeable due to non-virtual properties,
// but a real one works just fine, so make sure the headers use one of those.
A.CallTo(() => _request.Headers).Returns(new NameValueCollection());
_request.Headers["X-Requested-With"] = "XMLHttpRequest";
var httpContext = A.Fake<HttpContextBase>();
A.CallTo(() => httpContext.Request).Returns(_request);
_controller.ControllerContext = new ControllerContext(
new RequestContext(httpContext, new RouteData()),
_controller);
var result = _controller.Edit(1) as RedirectToRouteResult;
}
Be warned that there will be lots of pitfalls like this in the MVC framework, and continuing to fake them may continue to be frustrating. You may find a more sustainable approach is to extract as much of your logic as is feasible out into plain old testable business classes that don't rely on the MVC framework.

ASP.NET MVC ClaimsPrincipalPermission.CheckAccess Imperative Calls and Unit Tests

I've got code that uses CheckAccess with claims permission as so:
public ActionResult Index()
{
ClaimsPrincipalPermission.CheckAccess("foo", "bar");
return View();
}
I have a unit test like this:
[TestMethod]
public void TestIndex()
{
HomeController controller = new HomeController();
ViewResult result = controller.Index() as ViewResult;
Assert.IsTrue(result.View != null);
}
How do I mock ClaimsPrincipalPermission to always return successfully? I know that i can move this code to an attribute which will solve the problem. However, there are cases where I wish to call CheckAccess imperatively.
I would recommend wrapping this functionality into a dependency and using Dependancy Injection to make the code "Testable".
This involes "Extracting over overriding" this functionality into a different class and injecting the class into the controller.
By doing this removes your reliance on Static methods to an instance based method and will ensure you get a reliable result for testing purposes.
http://www.asp.net/mvc/tutorials/hands-on-labs/aspnet-mvc-4-dependency-injection

How to set up Ninject DI to create Hyprlinkr RouteLinker instances

I have an MVC4 Web API project and I making use of Mark Seemann's Hyprlinkr component to generate Uris to linked resources. (Customer -> Addresses for example).
I have already followed Mark's guide on Dependency injection with Web API (changing appropriately for Ninject) bit I can't quite work out what I should do to inject a IResourceLinker into my controllers.
Following Mark's guide my IHttpControllerActivator.Create create method looks like this:
IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = (IHttpController) _kernel.GetService(controllerType);
request.RegisterForDispose(new Release(() => _kernel.Release(controller)));
return controller;
}
It is in this method that the Hyprlinkr readme suggests to create the RouteLinker. Unfortunately I'm not sure how to register this with Ninject.
I can't just bind like below, as this results in multiple bindings:
_kernel.Bind<IResourceLinker>()
.ToMethod(context => new RouteLinker(request))
.InRequestScope();
I've got rebind working like this:
_kernel.Rebind<IResourceLinker>()
.ToMethod(context => new RouteLinker(request))
.InRequestScope();
But I'm concerned that changing the ninject binding graph is potentially a bad thing to do on every request.
What is the best way to achieve this?
Update following the request from Paige Cook
I'm using rebind here:
IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
_kernel.Rebind<IResourceLinker>()
.ToMethod(context => new RouteLinker(request))
.InRequestScope();
var controller = (IHttpController) _kernel.GetService(controllerType);
request.RegisterForDispose(new Release(() => _kernel.Release(controller)));
return controller;
}
IHttpControllerActivator.Create is called on every request. The rest of the bindings are made in the standard way, by standard I mean in the class generated by using the Ninject.MVC3 nuget package.
My controller looks like this:
public class CustomerController : ApiController
{
private readonly ICustomerService _customerService;
private readonly IResourceLinker _linker;
public CustomerController(ICustomerService customerService, IResourceLinker linker)
{
_customerService = customerService;
_linker = linker;
}
public CustomerModel GetCustomer(string id)
{
Customer customer = _customerService.GetCustomer(id);
if (customer == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return
new CustomerModel
{
UserName = customer.UserName,
Firstname = customer.Firstname,
DefaultAddress = _linker.GetUri<AddressController>(c => c.Get(customer.DefaultAddressId)),
};
}
}
Register a delegate Function to give you the linker
_kernel.Bind<Func<HttpRequestMessage, IResourceLinker>>()
.ToMethod(context => (request) => new RouteLinker(request));
Inject the delegate
readonly Func<HttpRequestMessage, IResourceLinker> _getResourceLinker;
public controller(Func<HttpRequestMessage, IResourceLinker> getResourceLinker) {
_getResourceLinker = getResourceLinker;
}
Use in your actions
public async Task<Thingy> Get() {
var linker = _getResourceLinker(Request);
linker.GetUri( ... )
}
If you only need to use RouteLinker from ApiController derivates, you don't really need to go through all the DI hoops.
You can just create it within the Controller like this:
var linker = new RouteLinker(this.Request);
IMO, using DI with RouteLinker first becomes valuable when you need a RouteLinker further down the stack - but then again, I also only use RouteLinker as a Concrete Dependency...
Thanks for adding the code sample. Based on what you have posted, you are running into your Bind/Rebind issue because you are issuing the _kernel.Bind<IResourceLinker> in the IHttpControllerActivtor.Create method every time.
You need to move the _kernel.Bind<IResourceLinker> to be registered the same way your are registering the rest of your bindings in the
...standard way, by standard I mean in the class generated by using the Ninject.MVC3 nuget package.
There should not be any need for the IResourceLinker to be binded multiple times, and this is why you are getting multiple instances, because the binding is firing every time a controller is created by the IHttpControllerActivator.
Update:
Sorry that I missed the need for an HttpRequestMessage as a constructor argument, I would go with Anthony Johnson's answer on this one.

Testing Account/Logon Action

I'm testing the Account/Loggon action using the built-in testing tool of Visual Studio 2010 and the class library from this article to create a fake controller context.
When I run the test method, this code line:
FormsAuthentication.SetAuthCookie(username, false);
throws an exception: Object reference not set to an instance of an object
To test the loggon action, I think I should create a controller with a fake controller context that has a cookie collection. Here is my testing code block:
AccountController controller = new AccountController();
var cookies = new HttpCookieCollection();
controller.ControllerContext = new FakeControllerContext(controller, cookies);
ActionResult result = controller.RemoteLogOn(username, password);
I'm not sure if this is the right way, but this is what we do, and it works.
Instead of directly using FormsAuthentication.SetAuthCookie, abstract it into an interface, e.g IFormsAuthenticationService, and implement as per regular.
Accept that in your MVC controllers where required, e.g:
public AccountController(IFormsAuthenticationService formsAuthenticationService)
{
_formsAuthenticationService = formsAuthenticationService; // should use DI here
}
public ActionResult LogOn(string username, string pw)
{
if (yourLogicWhichChecksPw)
_formsAuthenticationService.SetAuthCookie(username, false);
return RedirectToAction("Index");
}
Then in your unit-test, use something like Moq to fake out the interface.
var username = "blah";
var pw = "blah";
var fakesFormsAuth = new Mock<IFormsAuthenticationService>();
fakeFormsAuth.Verify(x => x.SetAuthCookie(username, false), Times.AtLeastOnce());
var controller = new AccountController(fakedFormsAuth.Object);
controller.LogOn(username, pw);
The reason for mocking this is because there is absolutely no need to unit-test Forms Authentication. It's a built-in, well tested and stable part of the ASP.NET framework. That's why we mock things where we don't care about the underlying implementation, instead we only test that certain conditions were met (it was called, exception was thrown, some variable was set, etc).
Test your own code, not the mechanics of .NET.
As for Stephen Walther's article, that's more for faking the RequestContext when certain code your testing expects data in the Request. Such as the User.Identity, Request.IsAuthenticated, Form variables, etc. That's where you need to fake the context, such as the following code:
public ActionResult Save(SomeModel)
{
var user = Request.User.Identity; // this will be null, unless you fake the context.
}

How do I unit test a custom ActionFilter in ASP.Net MVC

So I'm creating a custom ActionFilter that's based mostly on this project http://www.codeproject.com/KB/aspnet/aspnet_mvc_restapi.aspx.
I want a custom action filter that uses the http accept headers to return either JSON or Xml. A typical controller action will look like this:
[AcceptVerbs(HttpVerbs.Get)]
[AcceptTypesAttribute(HttpContentTypes.Json, HttpContentTypes.Xml)]
public ActionResult Index()
{
var articles = Service.GetRecentArticles();
return View(articles);
}
The custom filter overrides the OnActionExecuted and will serialize the object (in this example articles) as either JSON or Xml.
My question is: how do I test this?
What tests do I write? I'm a TDD novice and am not 100% sure what I should be testing and what not to test. I came up with AcceptsTypeFilterJson_RequestHeaderAcceptsJson_ReturnsJson(), AcceptsTypeFilterXml_RequestHeaderAcceptsXml_ReturnsXml() and AcceptsTypeFilter_AcceptsHeaderMismatch_ReturnsError406().
How do I test an ActionFilter in MVC that is testing the Http Accept Headers?
Thanks.
You just need to test the filter itself. Just create an instance and call the OnActionExecuted() method with test data then check the result. It helps to pull the code apart as much as possible. Most of the heavy lifting is done inside the CsvResult class which can be tested individually. You don't need to test the filter on an actual controller. Making that work is the MVC framework's responsibility.
public void AcceptsTypeFilterJson_RequestHeaderAcceptsJson_ReturnsJson()
{
var context = new ActionExecutedContext();
context.HttpContext = // mock an http context and set the accept-type. I don't know how to do this, but there are many questions about it.
context.Result = new ViewResult(...); // What your controller would return
var filter = new AcceptTypesAttribute(HttpContentTypes.Json);
filter.OnActionExecuted(context);
Assert.True(context.Result is JsonResult);
}
I just stumbled upon this blog post which seems the right way to me. He uses Moq.
What this chap is doing is mocking the HTTPContext, but also we need to set up a ContentType in the request:
// Mock out the context to run the action filter.
var request = new Mock<HttpRequestBase>();
request.SetupGet(r => r.ContentType).Returns("application/json");
var httpContext = new Mock<HttpContextBase>();
httpContext.SetupGet(c => c.Request).Returns(request.Object);
var routeData = new RouteData(); //
routeData.Values.Add("employeeId", "123");
var actionExecutedContext = new Mock<ActionExecutedContext>();
actionExecutedContext.SetupGet(r => r.RouteData).Returns(routeData);
actionExecutedContext.SetupGet(c => c.HttpContext).Returns(httpContext.Object);
var filter = new EmployeeGroupRestrictedActionFilterAttribute();
filter.OnActionExecuted(actionExecutedContext.Object);
Note - I have not tested this myself.

Resources