One of the benefits of Asp.Net MVC is that it allows you to write easily testable code. One thing that has always been notoriously difficult to test is the HttpContext object and its associated values – the Request and Response etc. This is why I didn’t like it when dependencies on HttpContext started to creep into our Controller actions, as it meant that our Controllers became harder to test. Having to set up mock HttpContexts for each set of controller tests started to get annoying and any barrier to writing tests is a bad thing in my book – especially as we’d spent a real effort on my current project establishing BDD amongst the team.

One of the ways in which this can be avoided is to use the built in ActionFilters in Asp.Net as a kind of cheap AOP – attributing your actions with filters that will “inject” the necessary values into your action’s parameters at runtime. This means your action can be changed to just accepting the values it needs as method parameters, allowing you to test them really easily.

Here’s a simple example:

public ActionResult GetHttpReferrer()
{
    ViewData.Add("urlReferrer",
        ControllerContext.HttpContext
            .Request.UrlReferrer.ToString());
    return View();
}

This action just displays a view containing the UrlReferrer string. It has to access the HttpContext via the ControllerContext, which means that in order to test this action we would need to set up a mock or fake HttpContext with a mock or fake Request object and add them to the ControllerContext before we can test the action. This is fine if we’re doing this once, but chances are that another action or another controller will need to do a similar thing.

Enter the ActionFilter.

To remove the dependeny on HttpContext in the controller action, we can create an ActionFilter that provides the action with this value at runtime. Here’s a simple ActionFilter that adds the url referrer string to the action parameters:

using System.Web.Mvc;

public class HttpReferrerAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting
        (ActionExecutingContext filterContext)
    {
        filterContext.ActionParameters["urlReferrer"] =
            filterContext.HttpContext.Request.UrlReferrer.ToString();
    }
}

All we need to do is override the OnActionExecuting() method, which will get called before the action gets called. At this point we can access the HttpContext and add the value we need into the action’s collection of parameters, which the ActionFilter knows about.

We can write tests for this ActionFilter by creating a fake or mock HttpContext and adding it to the ActionExecutingContext that gets passed in to the OnActionExecuting method(). Whilst this is the same as having to set up the dependencies for the controller tests, the point is that we only have to do this once for this ActionFilter, which can then be re-used amongst controllers. It keeps the controllers free of dependencies on HttpContext and makes the code cleaner and more maintainable.

I use JP Boohoo’s style of BDD specifications for testing, which is basically a set of extension methods that add syntactic sugar to your test code. My specification for the HttpReferrerAttribute is as follows:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using SpecHelpers;
using Observation = MbUnit.Framework.TestAttribute;

public class when_the_http_referrer_attribute_is_told_to_execute
    : ContextSpecification<HttpReferrerAttribute>
{
    private ActionExecutingContext filter_context;
    private HttpContextBase http_context;
    private HttpRequestBase request;
    private Uri url_referrer;

    protected override HttpReferrerAttribute create_sut()
    {
        return new HttpReferrerAttribute();
    }

    protected override void establish_context()
    {
        url_referrer = new Uri("http://thelastplaceicamefrom.com");

        filter_context = new ActionExecutingContext();
        filter_context.ActionParameters =
            new Dictionary<string, object>();
        filter_context.ActionParameters.Add("urlReferrer", null);

        request = dependency<HttpRequestBase>();
        http_context = dependency<HttpContextBase>();

        request.setup_result(r => r.UrlReferrer)
            .Return(url_referrer);
        http_context.setup_result(c => c.Request)
            .Return(request);

        filter_context.HttpContext = http_context;

        sut = create_sut();
    }

    protected override void because()
    {
        sut.OnActionExecuting(filter_context);
    }

    [Observation]
    public void should_set_the_url_referrer_parameter_correctly()
    {
        filter_context
            .ActionParameters["urlReferrer"]
                .should_be_equal_to(url_referrer.ToString());
    }
}

A few things to note here – the dependency<>() extension method is just wrapping a call to RhinoMocks to create a mock object that we’re using in the test:

protected virtual InterfaceType dependency<InterfaceType>()
    where InterfaceType : class
{
    return MockRepository.GenerateMock<InterfaceType>();
}

The set_up_result() extension method is setting up a RhinoMocks expectation on the mock object:

public static IMethodOptions<R> setup_result<T, R>
    (this T mock, Function<T, R> func) where T : class
{
    return mock.Expect(func).Repeat.AtLeastOnce();
}

And the should_be_equal_to() extension method is simply wrapping a call to an MBUnit (or NUnit if you prefer) assert statement:

public static void should_be_equal_to<T>(this T actual, T expected)
{
    Assert.AreEqual(expected, actual);
}

Back to the Action.

As we have delegated the responsibility of retrieving the url referrer to the ActionFilter, our original controller action now looks like this:

[HttpReferrer]
public ActionResult GetHttpReferrer(string urlReferrer)
{
    ViewData.Add("urlReferrer", urlReferrer);
    return View();
}

Which means it can be tested really easily without any dependency on HttpContext. Our full specification for the controller now looks like this:

using System.Web.Mvc;
using Filters;
using SpecHelpers;
using Observation = MbUnit.Framework.TestAttribute;

public class when_the_home_controller_displays_the_get_referrer_view
    : ContextSpecification<HomeController>
{
    private string url_referrer;
    private ActionResult result;

    protected override HomeController create_sut()
    {
        return new HomeController();
    }

    protected override void establish_context()
    {
        url_referrer = "http://thelastplaceicamefrom.com";

        sut = create_sut();
    }

    protected override void because()
    {
        result = sut.GetHttpReferrer(url_referrer);
    }

    [Observation]
    public void should_populate_the_view_with_the_url_referrer()
    {
        (result as ViewResult)
            .ViewData["urlReferrer"]
                .should_be_equal_to(url_referrer);
    }

    [Observation]
    public void should_retrieve_url_referrer_from_the_http_context()
    {
        typeof(HomeController)
            .GetMethod("GetHttpReferrer")
                .should_be_attributed_with<HttpReferrerAttribute>();
    }
}

The first observation checks that the url referrer is being passed to the view (via the ViewData). Note that there are no dependencies on HttpContext for this controller, which makes it incredibly easy to test.

The second observation checks that our ActionFilter is actually going to inject the correct url referrer value at runtime into our parameter – i.e. we have applied the parameter to the action! The should_be_attributed_with() extension method simply wraps another call to an MBUnit assert that the method signature has the custom attribute:

public static void should_be_attributed_with<T>
    (this MethodInfo method) where T : Attribute
{
    method.GetCustomAttributes(typeof(T), false)
        .Length.should_be_equal_to(1);
}

So now if we need to access the url referrer in another action all we need to do is apply the ActionFilter attribute and we know it will be provided at runtime. We can also write specifications to check that this attribute is present.

Summary.

Hopefully this has shown that ActionFilters are a great way to inject parameters into controller actions at runtime. This means they can be used to retrieve and provide run-time dependent values (e.g. from HttpContext) to the controllers which keeps the controllers clean, simple and easily tested. The result is reusable, crossing cutting functionality, with clear separation of concerns that makes for a maintainable, testable solution.

A full working MVC project example for this code is available on CodePlex at:

http://jamesbroome.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=32061