Inversion of Control, ASP.NET MVC and Unit Testing

An entry about asp.net mvc | tdd | design patterns Publication date 18. April 2008 23:09

A couple of days ago, a new preview of the ASP.NET MVC framework was made available on Codeplex. It contains some very welcome changes that makes testing controllers much more enjoyable than before. You can read all about the changes over at Scott Guthries blog; in this post I'd like to show a quick demonstration of how we can use the inversion of control pattern to streamline writing testable controllers. If you're not familiar with the concepts of IOC, it can be explained as a design pattern which comes in several flavours, all of which aim to help you write more loosely coupled code. To get started, "Inversion of Control and the Dependency Injection pattern" by Martin Fowler is a must-read, and Jimmy Bogard has a great series of posts explaining the pattern and the principles from which it came to be.

With that out of the way, let's get on with our demonstration. Let's assume that we're tasked with building a blog engine. One of the things we'll need, is an action that can display a single blog post. Good TDD citizens that we are, let's start off by writing a test first:

[TestMethod]
public void ShowPost()
{
    const string title = "Some Post Title";
    const string urlTitle = "Some-Post-Title";
 
    // set up mocks
    Mock<IPost> article = new Mock<IPost>();
    Mock<IPostRepository> repository = new Mock<IPostRepository>();
    repository.Expect(r => r.GetPost(title)).Returns(article.Object);
 
    // excersize method under test
    PostController controller = new PostController(repository.Object);
    RenderViewResult result = controller.ShowPost(urlTitle) as RenderViewResult;
 
    // verify result of test
    Assert.IsNotNull(result);
    Assert.AreSame(article.Object, result.ViewData);
    Assert.AreEqual("Post", result.ViewName);
}

 

Okay, so when writing the test I was forced to make some decisions on the architecture of my blog engine. The first thing I realized, was that I'd need some entity to represent a blog post. For this, I created an interface IPost. Then, I realized I'd need some way of locating a post from the database, so I created the IPostRepository interface and gave it a single method; GetPost. This method takes the title of the post as its only argument, because I knew that I'd want the URL's in my blog to look something like http://myblog.com/Posts/Some-Post-Title.

Digression: The Importance of Testing in Isolation

The great thing up until now, is that I've not written a single line of actual code - all I've done is define a few interfaces and basically draw up the blueprints of the application. This is important with respect to test-driven development; here I'm writing a test to help me implement the ShowPost action method on the PostController class, but to implement that method I come across dependencies to other classes (which might not even be implemented yet, as is the case here), and I need to take them out of the equation so that I can write my test without it depending on their implementations (and inherently, their correctness). Notice that I've used a mocking library (Moq) to help me accomplish this. Testing in isolation is one of the most important aspects of writing useful unit tests; I cannot stress that enough. If you don't isolate your tests and you introduce a bug somewhere, then not only the test that verifies that particular code path will fail, but loads of tests all over the place will fail because they too depend on the code containing the bug. Not only will it be much harder to track down the offending line(s) of code, but seeing a sea of tests go red doesn't exactly do wonders for your motivation either. Unit testing should be like playing reverse Domino; the goal is to not knock over any other pieces if one should tip over.

Back on Track...

Okay, digression aside. So, if you've played about with the previous beta releases of ASP.NET MVC, you probably noticed that our controller action method now has a return value - an ActionResult object, which we've cast to a RenderViewResult object. This is one of the changes that have been done in the new version, which drastically simplifies unit testing. If you've tried to write tests for controllers previously, you may also notice the distinct lack of any code in my test to deal with mocking the Http context and all that stuff - we don't need to anymore (Woo!). I can just capture the return value of calling the action method and use it to assert that the action set the right view data and requested the right view, and so forth.

Running the test now should make it fail - we've yet to actually implement the ShowPost method on the PostController. That's easy to remedy, however:

public class PostController : Controller
{
    private readonly IPostRepository _repository;
 
    public PostController(IPostRepository repository)
    {
        _repository = repository;
    }
 
    public ActionResult ShowPost(string title)
    {
        title = title.Replace("-", " ");
 
        IPost post = _repository.GetArticle(title);
        return RenderView("Post", post);
    }
}

 

Resolving Dependencies

For our test, we manually injected the IPostRepository dependency into the PostController - but that won't do when we actually browse to the URL routed to the action; out of the box, ASP.NET MVC won't know how to create an instance of the PostController because its constructor is not parameterless. That's where an IOC container comes into the picture - it'll let us register all the dependencies our application requires in one single place, making it easy to manage and maintain them, and then it'll take care of figuring out which dependencies goes where when instantiating objects. As I mentioned previously, I really dig Autofac, so I'll be demonstrating how we'd do things using it.

Autofac with ASP.NET MVC

There's already a page in the Autofac wiki which goes through this, so I'll just quickly skim through it here for completeness. The first thing we'll need to do, is register a couple of Http modules in the web.config file, which Autofac needs to do its magic:

<httpModules>
  <add name="ContainerDisposal" type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web"/>
  <add name="PropertyInjection" type="Autofac.Integration.Web.PropertyInjectionModule, Autofac.Integration.Web"/>

 

That done, we'll jump to the Global.asax file. Autofac comes with an already ready ASP.NET MVC integration module, so all we need to do is to hook things up. That means our Global.asax file will look like this:

public class GlobalApplication : HttpApplication, IContainerProviderAccessor
{
    static IContainerProvider _containerProvider;
 
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.MapRoute(
            "Article",
            "Articles/{*title}",
            new { controller = "Article", action = "ShowPost" },
            new { title = @"[^\.]*" }
        );
    }
 
    protected void Application_Start()
    {
        // when the application starts, we need to build the container and register all
        // the dependencies that we want it to manage for us
        var builder = new ContainerBuilder();
 
        // we register the implementation of our IPostRepository as a singleton
        builder.Register<SqlPostRepository>().As<IPostRepository>().SingletonScoped();
 
        // and then we'll use the AutofacControllerModule to register all the controllers it finds in the given assembly
        builder.RegisterModule(new AutofacControllerModule(Assembly.GetExecutingAssembly()));
 
        // finally we build the container...
        _containerProvider = new ContainerProvider(builder.Build());
 
        // ...and notify ASP.NET MVC that it should use Autofacs own controller factory to instantiate controllers,
        // so that Autofac can resolve any dependencies the controllers need
        ControllerBuilder.Current.SetControllerFactory(new AutofacControllerFactory(_containerProvider));
 
        RegisterRoutes(RouteTable.Routes);
    }
 
    public IContainerProvider ContainerProvider
    {
        get { return _containerProvider; }
    }
}

 

The interesting part is the Application_Start method. Here, we instantiate the container and register all the dependencies (well, we just have the one, IPostRepository, but you get the idea). We also register all the controllers, because we want the container to be in charge of instantiating these, so that it can resolve any dependencies they rely on. To make this work, we tell ASP.NET MVC to use the Autofac controller factory instead of its own. Now, whenever a request comes in, Autofac will be in charge of instantiating the controller, and it will scan the constructor and look up any dependencies that are required and inject them accordingly. And that's it; we have a controller that is a breeze to decouple from its dependencies, making it easily testable in isolation, and with an IOC container in place all the complexity of loose coupling the dependencies is taken off our hands. Oh, how I wish all the code I worked on was like this (damn you, legacy code! :p).

Currently rated 3.3 by 12 people

  • Currently 3.25/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Comments

Powered by BlogEngine.NET 1.4.5.0

Welcome!

My name is Fredrik Kalseth, and this is my blog - thanks for visiting! I am fortunate enough to work with what I love for a living, and this blog is essentially the biproduct of that.

I work as a senior consultant for Capgemini, and am also an active participant in the Norwegian .NET community, as an avid attendee but also as a speaker (most recently at NNUG and MSDN Live).

As a developer, I have a wide circle of interest. My primary passion is for agile, test-driven development, with focus on best practices and clean code. That said, I also love to work on the frontend, especially with web development.

On Twitter? My handle is fkalseth. On LinkedIn? I`m there too.


Disclaimer

This is a personal blog; any opinions expressed here are my own and do not necessarily reflect those of my employer. All content herein is my own original creation, and as such is protected by copyright law. Unless otherwise stated, all source code posted on this blog is freely usable under the Microsoft Permissive License.

What Readers Talk About

Comment RSS