Asp.Net MVC – Using ActionFilters to remove HttpContext from your controllers

Tags: , , , , No Comments »

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

Asp.Net MVC – Separation of concerns amongst team members

Tags: , , , , 1 Comment »

One of the clear advantages of using the MVC pattern is that it encourages separation of concerns – it doesn’t enforce it by any means, but following the pattern results in a nice clean, maintainable, flexible solution. The architectural advantages of this are documented everywhere, but I also believe that it aids separation of concerns in another way – in the roles of the project team members.

I’m lucky enough to work in an environment where we have a team made up of .Net developers (C#), UI developers (HTML, CSS, Javascript), and database developers (SQL). Each of these roles performs a different function, has a different skill set, work on separate parts of the application and have separate tasks during a sprint.

In the past, when working with WebForms, as a .Net developer I would spend a large amount of time in the UI layer, adding user controls to .aspx markup, and trying to explain to the UI developer how the “cssclass” attribute worked, why elements where rendered differently at runtime than they appeared in the code editor etc etc. It all felt a bit wrong. Because we are using Asp.Net MVC, on my current project we can both work side by side on a feature without treading on each others toes. We can deliver quicker and we have less breaking changes. I don’t put .Net code in the UI mark-up and the UI developer doesn’t put CSS styles in .Net code.

On my current project, our UI developer didn’t join the team until after Sprint 4 – that’s 8 weeks of development without a proper UI in place. However, at the end of each of those 2 week sprints, we presented our application to the client as working software and planned out what we were going to do next, all the time, completely ignoring the UI. We were lucky in that our client understood and trusted us when we said that the UI would be designed and built without any impact to the underlying functionality we had already developed.

And, after Sprint 4, when our UI developer joined the team, we went from presenting an application that looked like an out-of-the-box Asp.Net MVC application you get in Visual Studio, to a rich, professional, fully branded website in the space of one two-week sprint.  He was able to completely re-skin the entire UI using HTML and CSS (helped by the fact that we’re using the beautiful Spark View Engine) without me having to change a single line of C#. Now that’s what I call separation of concerns.

Design by j david macor.com.Original WP Theme & Icons by N.Design Studio