TDD and Dependency Injection with ASP.NET MVC

One of the guiding principles in the design of the new ASP.NET MVC Framework is enabling TDD (Test Driven Development) when building a web application. While still leaving room for improvement, the soon to be released CTP (Community Technology Preview) release of ASP.NET MVC will do a pretty nice job in supporting the TDD habit.

This post will provide a detailed walkthrough of building a web application in a Test Driven manner while also demonstrating one way to integrate a Dependency Injection (DI) Framework into an ASP.NET MVC app. At the very end, you can download the code.

I chose StructureMap 2.0 for the DI framework simply because I’m familiar with it and it requires very little code and configuration. If you’re interested in an example using Spring.NET, check out Fredrik Normen’s post. I’ll try and post code examples in the future using Castle Windsor and ObjectBuilder.

Start Me Up! with apologies to The Rolling Stones

Once the CTP is released and you have it installed, open Visual Studio 2008 and select the File | New Project menu item. In the dialog, select the ASP.NET MVC Web Application and Test project template.

New Project Dialog

If you prefer, you can swap out the test project we create with a regular class library and use MbUnit, NUnit, xUnit, or any other unit test framework of your choice for testing. My test framework of choice is MbUnit, but out of sheer laziness I’ll stick with MSTest for this example. UPDATE: I have included an example using MbUnit you can download at the bottom.

As you might guess, I’ll start off building the canonical blog demo. I am going to start without using a database. We can always add that in later.

The first thing I want to do is add a few classes to the main project. I won’t add any implementation yet, I just want something to compile against. I’m going to add the following:

  • BlogController.cs to the Controllers directory
  • IPostRepository.cs to the Models directory
  • Post.cs to the Models directory
  • BlogControllerTests to the MvcApplicationTest project.

After I’m done, my project tree should look like this.

 Project Tree

At this point, I want to implement just enough code so we can write a test. First, I define my repository interface.

using System;
using System.Collections.Generic;

namespace MvcApplication.Models
{
  public interface IPostRepository
  {
    void Create(Post post);

    IList<Post> ListRecentPosts(int retrievalCount);
  }
}

Not much of a blog post repository, but it’ll do for this demo. When you’re ready to write the next great blog engine, you can revisit this and add more methods.

Also, I’m going to leave the Post class empty for the time being. We can implement that later. Let’s implement the blog controller next.

using System;
using System.Web.Mvc;

namespace MvcApplication.Controllers
{
  public class BlogController : Controller
  {
    [ControllerAction]
    public void Recent()
    { 
      //Wait! Gotta write a test first...
    }
  }
}

Ok, we better stop here. We’ve gone far enough without writing a unit test. After all, I’m supposed to be demonstrating TDD. Let’s write a test.

Let’s Get Test Started, In Here. with apologies to the Black Eyed Peas

Starting with the simplest test possible, I’ll make sure that the Recent action selects the correct view.

For simplicity, I’ll use the Test-Specific Subclass pattern.

[TestClass]
public class BlogControllerTests
{
  [TestMethod]
  public void BlogControllerSelectsCorrectView()
  {
    BlogControllerDouble controller = new BlogControllerDouble();
    controller.Recent();
    Assert.AreEqual("Recent", controller.SelectedView);
  }

  private class BlogControllerDouble : BlogController
  {
    public string SelectedView { get; private set; }
            
    protected override void RenderView(string viewName
      , string masterName
      , object viewData)
    {
      this.SelectedView = viewName;
      //I don’t care about masterName at this point.
      //I don’t care about viewData at this point.
    }
  }
}

When I run this test, the test fails.

Test Results

This is what we expect, after all, we haven’t yet implemented the Recent method. This is the RED part of the RED, GREEN, REFACTOR rhythm of TDD.

Let’s go and implement that method.

[ControllerAction]
public void Recent()
{
  //Note we haven’t yet created a view
  RenderView("Recent");
}

Notice that at this point, we’re focusing on the behavior of our app first rather than focusing on the UI first. This is a stylistic difference between ASP.NET MVC and ASP.NET WebForms. Neither one is necessarily better than the other. Just a difference in approach and style.

Now when I run my unit test, it passes.

A Passing Test

Ok, so that’s the GREEN part of the TDD lifecycle and a very very simple demo of TDD. Let’s move to the REFACTOR stage and start applying Dependency Injection.

It’s Refactor Time! with apologies to the reader for stretching this theme too far

In order to obtain the recent blog posts, I want to provide my blog controller with a “service” instance it can request those posts from. 

At this point, I’m not sure how I’m going to store my blog posts. Will I use SQL? XML? Stone Tablet?

Dunno. Don’t care... yet.

We can delay that decision till the last responsible moment. For now, I’ll create a repository abstraction to represent how I will store and retrieve blog posts in the form of an IPostRepository interface. We’ll update the blog controller to accept an instance of this interface in its constructor.

This is the dependency part of Dependency Injection. My controller now has a dependency on IPostRepository. The injection part refers to the mechanism you use to pass that dependency to the dependent class as opposed to having the class create that instance directly and thus binding the class to a specific implementation of that interface.

Here’s the change to my BlogController class.

public class BlogController : Controller
{
  IPostRepository repository;

  public BlogController(IPostRepository repository)
  {
    this.repository = repository;
  }

  [ControllerAction]
  public void Recent()
  {
    //Note we haven’t yet created a view
    RenderView("Recent");
  }
}

Great. Notice I haven’t changed Recent yet. I need to write another test first. This will make sure that we pass the proper data to the view.

Note: If you're following along, you’ll notice that the first test we wrote won't compile. Comment it out for now. We can fix it later.

First, I need to refactor my test double a bit, adding a property to access the passed in view data. Changes are in bold.

private class BlogControllerDouble : BlogController
{
  public BlogControllerDouble(IPostRepository repository)
    : base(repository)
  { }

  public string SelectedView { get; private set; }
  public object RenderedViewData { get; private set; }
    
  protected override void RenderView(string viewName
    , string masterName
    , object viewData)
  {
    this.SelectedView = viewName;
    //I don’t care about masterName at this point.
    this.RenderedViewData = viewData;
  }
}

Ok, I’m now ready to write a new test. I’m going to use a mock framework, so before I write this test, I need to reference RhinoMocks.dll in my test project, downloaded from the Rhino Mocks downloads page.

Note: I’ve included this assembly in the example project at the end of this post.

Here’s the new test.

[TestMethod]
public void BlogControllerPassesCorrectViewData()
{
  MockRepository mocks = new MockRepository();
  IPostRepository repository = mocks.DynamicMock<IPostRepository>();
  SetupResult
    .For(repository.ListRecentPosts(0))
    .IgnoreArguments()
    .Return(new List<Post>(new Post[] {new Post(), new Post()}));
  mocks.ReplayAll();
    
  BlogControllerDouble controller = new BlogControllerDouble(repository);
  controller.Recent();
  IList<Post> posts = (IList<Post>)controller.RenderedViewData;
  Assert.AreEqual(2, posts.Count, "Expected two posts");
}

What this test is doing is dynamically stubbing out an implementation of the IPostRepository interface. We then tell it that no matter what argument is passed to ListRecentPosts, return two posts. We can then pass that stub to our controller.

Note: We haven’t yet needed to implement this interface. We don’t need to yet. We’re interested in isolating our test to only test the logic in the action method, so we fake out the interface for the time being.

At this point, the test fails as we expect. We need to refactor Recent to do the right thing now.

[ControllerAction]
public void Recent()
{
  IList<Post> posts = 
    this.repository.ListRecentPosts(10); //Doh! Magic Number!
  RenderView("Recent", posts);
}

Now when I run my test, it passes!

Inject That Dependency

But we’re not done yet. When I load up a browser and try to navigate to this controller action (on my machine, http://localhost:64701/blog/recent/), I get the following error page.

No parameterless constructor defined for this object. - Mozilla Firefox

Well of course it errors out! By default, ASP.NET MVC requires that controllers have a public parameterless constructor so that it can create an instance of the controller. But our constructor requires an instance of IPostRepository. We need someone, anyone, to pass such an instance to our controller.

StructureMap (or DI framework of your choice) to the rescue!

Note: Make sure to download and reference the StructureMap.dll assembly if you’re following along. I've included the assembly in the source code at the end of this post.

The first step I’m going to do is create a StructureMap.config file and add it to the root of my application. Here are the contents of the file.

<?xml version="1.0" encoding="utf-8" ?>
<StructureMap>
  <Assembly Name="MvcApplication" />
  <Assembly Name="System.Web.Extensions
              , Version=3.6.0.0
              , Culture=neutral
              , PublicKeyToken=31bf3856ad364e35" />

  <PluginFamily Type="System.Web.Mvc.IController" 
                Assembly="System.Web.Extensions
                  , Version=3.6.0.0
                  , Culture=neutral
                  , PublicKeyToken=31bf3856ad364e35">
    <Plugin Type="MvcApplication.Controllers.BlogController" 
            ConcreteKey="BlogController" 
            Assembly="MvcApplication" />
  </PluginFamily>

  <PluginFamily Type="MvcApplication.Models.IPostRepository" 
                Assembly="MvcApplication" 
                DefaultKey="InMemory">
    <Plugin Assembly="MvcApplication" 
            Type="MvcApplication.Models.InMemoryPostRepository" 
            ConcreteKey="InMemory" />
  </PluginFamily>

</StructureMap>

I don’t want to get bogged down in describing this file in too much detail. If you want a deeper understanding, check out the StructureMap documentation.

The bare minimum you need to know is that each PluginFamily node describes an interface type and a key for that type. A Plugin node describes a concrete type that will be used when an instance of the family type needs to be created by the framework.

For example, in the second PluginFamily node, the interface type is  IPostRepository which we defined. The concrete type is InMemoryPostRepository. So anytime we use StructureMap to construct an instance of a type that has a dependency on IPostRepository, StructureMap will pass in an instance of InMemoryPostRepository.

Well if that’s true, we better then create that class. Normally, I would use a SqlPostRepository. But for purposes of this demo, we’ll store blog posts in memory using a static collection. We can always implement the SQL version later.

Note: This is where I would normally write tests for InMemoryPostRepository but this post is already long enough, right? Don’t worry, I included unit tests in the downloadable code sample.

public class InMemoryPostRepository : IPostRepository
{
  //simulates database storage
  private static IList<Post> posts = new List<Post>();

  public void Create(Post post)
  {
    posts.Add(post);
  }

  public System.Collections.Generic.IList<Post> 
    ListRecentPosts(int retrievalCount)
  {
    if (retrievalCount < 0)
      throw new ArgumentOutOfRangeException("retrievalCount"
          , "Let’s be positive, ok?");

    IList<Post> recent = new List<Post>();
    int recentIndex = posts.Count - 1;
    for (int i = 0; i < retrievalCount; i++)
    {
      if (recentIndex < 0)
        break;
      recent.Add(posts[recentIndex--]);
    }
    return recent;
  }

  public static void Clear()
  {
    posts.Clear();
  }
}

Quick, We Need A Factory

We’re almost done. We now need to hook up StructureMap to ASP.NET MVC by writing implementing IControllerFactory. The controller factory is responsible for creating controller instances. We can replace the built in logic with our own factory.

public class StructureMapControllerFactory : IControllerFactory
{
  public IController CreateController(RequestContext context
    , Type controllerType)
  {
    try
    {
      return ObjectFactory
        .GetNamedInstance(controllerType.Name);
    }
    catch (StructureMapException)
    {
      //Use the default logic.
      return (IController)Activator.CreateInstance(controllerType);
    }
  }
}

Finally, we wire it all up together by adding the following method call within the Application_Start method in Global.asax.cs.

protected void Application_Start(object sender, EventArgs e)
{
  ControllerBuilder.Current
    .SetDefaultControllerFactory(typeof(StructureMapControllerFactory));

  //... Route Code, other stuff...
}

And we’re done! Now that we have hooked up the dependency injection framework into our application, we can revisit our site in the browser (after compiling) and we get...

could-not-find-view

Excellent! Despite the yellow screen of death, this is a good sign. We know our dependency is getting injected because this is a different error message than we were getting before. This one in particular is informing us that we haven’t created a view for this action. So we need to create a view.

Sorry! Out of scope. Not in the spec.

I leave it as an exercise for the reader to create a view for the page. If you need help with that, read part 3 of Scott Guthrie’s excellent series on the ASP.NET MVC Framework.

Although this example was a ridiculously simple application, the principle applies in building a larger app. Just take the techniques here and rinse, recycle, repeat your way to TDD nirvana.

To see the result of all this, download the source code.

UPDATE: Realizing that not everyone has Visual Studio 2008 Professional with the testing integration, I have included a variant of this example using MbUnit.

Note: The code obviously won’t work until the CTP is released and you’ve installed it. It will require ASP.NET 3.5 to be installed.

[ad] Free Bug Tracking & Project Management Software Axosoft’s OnTime 2007 allows software development teams to collaborate on software projects by tracking everything from defects to enhancements to helpdesk incidents in one easy-to-use database driven by an intuitive Windows, Web or VS.NET Integrated UI. Get a Free Single-User License ($200 Value!)

What others have said

Requesting Gravatar... Evan Dec 07, 2007 9:23 PM
# re: TDD and Dependency Injection with ASP.NET MVC
You rock! ;-)
Requesting Gravatar... Jeffrey Palermo Dec 07, 2007 9:46 PM
# re: TDD and Dependency Injection with ASP.NET MVC
@Phil,
The first item to get committed to the trunk of mvccontrib was a StructureMapControllerFactory.

You can check it out here:http://www.codeplex.com/MVCContrib

We're going to go public and advertise more once we can build against the public CTP. Then it will make more sense for the public to use it.
Requesting Gravatar... Fredrik Normén Dec 07, 2007 11:23 PM
# re: TDD and Dependency Injection with ASP.NET MVC
Your blog post is so much nicer than mine ;)

Something that I don’t like is that the ControllerFactory will get the Type of the Controller. You need here as with Spring.Net use the Type.Name to get the name and let another framework once again locate the type and then create an instance of the controller. I think it’s the ControllerFactory’s responsibility to locate the Controller and that you should make sure to pass the name of the controller as the argument to the factory, not the type.
Requesting Gravatar... Haacked Dec 08, 2007 12:31 AM
# re: TDD and Dependency Injection with ASP.NET MVC
@Fredrik - Yes, I saw your comment in Connect. I thought I responded to it. We'll change that in the next milestone. Thanks for submitting the feedback! :)
Requesting Gravatar... DotNetKicks.com Dec 08, 2007 2:56 AM
# TDD and Dependency Injection with ASP.NET MVC
You've been kicked (a good thing) - Trackback from DotNetKicks.com
Requesting Gravatar... Jeremy D. Miller Dec 08, 2007 10:25 AM
# re: TDD and Dependency Injection with ASP.NET MVC
Cool post, says the horribly biased guy.

Just so folks with angle bracket allergies know, you don't have to use Xml configuration for the simple use cases with StructureMap anymore:

http://codebetter.com/blogs/jeremy.miller/archive/2007/04/09/Introduction-to-StructureMap-2.0-_2800_Part-1_2900_.aspx
Requesting Gravatar... ScottGu's Blog Dec 08, 2007 12:14 PM
# December 8th Links: ASP.NET, ASP.NET AJAX, ASP.NET MVC, .NET, VS 2008
Here is the latest in my link-listing series .&amp;#160; Also check out my ASP.NET Tips, Tricks and Tutorials
Requesting Gravatar... Sean Chambers Dec 08, 2007 12:44 PM
# re: TDD and Dependency Injection with ASP.NET MVC
Excellent stuff Phil!

I am definately looking forward to this. Even though I am very biased and love monorail. I think you guys have finally hit the nail on the head this time. Couple this with the fact that you are now at MS, I think this will be very good! =)

The only thing I really don't like, and I'm sure you've heard this already, is the fact that every action requires a [ControllerAction] attribute. IMO, this is a lot of extra typing that isn't necessarily needed. I know you guys are trying to help out by making sure the developer knows what exactly they are exposing, but education would be more powerful here than requiring a specific attribute on every method. Just my 2 cents tho!

Is there anyway that we can make it so any public methods are automatically set as actions in a configuration or something along those lines?

Also, is there a way to set values to send back to the view in an array rather than passing them to RenderView? Like PropertyBag[] in MonoRail?

Other then that everything looks pretty good! Great Job!! Looking forward to the CTP
Requesting Gravatar... Sean Chambers Dec 08, 2007 12:59 PM
# Phil Haack posts about ASP.NET MVC
Phil Haack has a tutorial type post of usage of the new MS MVC with TDD, DI and the Repository Pattern
Requesting Gravatar... Haacked Dec 08, 2007 2:31 PM
# re: TDD and Dependency Injection with ASP.NET MVC
@Sean - thanks for the feedback. Yep, I've heard a lot about the [ControllerAction] attribute from some people. Rather than attempt to address it everytime it comes up, I'll write a blog post that addresses that single item.

Keep in mind though, we'd love to hear feedback on other areas of the framework. It seems that one thing gets a disproportionate amount of attention. I have to say, if that's the only thing we get "wrong", then I'm happy. But I would bet there are bigger things we missed and I hope to hear feedback on them when the CTP is released.
Requesting Gravatar... Javier Lozano Dec 08, 2007 2:47 PM
# re: TDD and Dependency Injection with ASP.NET MVC
Phil, great post! I've always liked your style of writing. You're very good at taking abstract concepts and explain them in terms people can relate to.

This is a great asset for people trying to grok the benefits of using ASP.NET MVC!
Requesting Gravatar... Jason Haley Dec 08, 2007 8:11 PM
# Interesting Finds: December 8, 2007
Requesting Gravatar... Christopher Steen Dec 08, 2007 10:20 PM
# Link Listing - December 8, 2007
ASP.NET TDD and Dependency Injection with ASP.NET MVC [Via: Haacked ] Link Blogs Silverlight Cream...
Requesting Gravatar... Tony Testa's World Dec 08, 2007 11:16 PM
# ASP.NET MVC Framework (Delayed)
ASP.NET MVC Framework (Delayed)
Requesting Gravatar... ScottGu's Blog Dec 09, 2007 8:55 PM
# ASP.NET 3.5 Extensions CTP Preview Released
Earlier today we released the first CTP preview of an &amp;quot;ASP.NET 3.5 Extensions&amp;quot; release that
Requesting Gravatar... Marlon Grech Dec 09, 2007 10:55 PM
# re: TDD and Dependency Injection with ASP.NET MVC
This is totally cool... You are the BEST!!!!!
Requesting Gravatar... Maor David Dec 10, 2007 2:54 AM
# ASP.NET 3.5 Extensions Preview Released
The ASP.NET 3.5 Extensions Preview is a preview of new functionality being added to ASP.NET 3.5 and ADO
Requesting Gravatar... Javier-Romero Dec 10, 2007 6:04 AM
#
ASP.NET 3.5 Extensions CTP Preview Lanzado ...
Requesting Gravatar... Eilon Lipton's Blog Dec 10, 2007 6:30 PM
# ASP.NET MVC Design Philosophy
This week the first preview of the ASP.NET MVC framework was released to the web as part of the ASP.NET
Requesting Gravatar... di .NET e di altre Amenit Dec 12, 2007 12:44 AM
# ASP.NET 3.5: Un po' di link alla rinfusa #4
ASP.NET 3.5: Un po' di link alla rinfusa #4
Requesting Gravatar... Matt Blodgett Dec 12, 2007 8:02 AM
# re: TDD and Dependency Injection with ASP.NET MVC
As a neophyte interested in unit testing and dependency injection, I have to admit that I'm really taken aback by the amount of _stuff_ you had to write to get this simple bit of functionality set up.

I'm struggling to understand how it could possibly be worth it. Would someone please enlighten me?
Requesting Gravatar... Haacked Dec 12, 2007 9:15 AM
# re: TDD and Dependency Injection with ASP.NET MVC
@Matt You should search my blog for TDD and you'll see I've written extensively about the benefits of TDD.

AS for this example, I was taking an extremely detailed "first-principles" style approach. So in the real-world, the code would be much less in the test by employing helper methods and the like.

Also, some of the extra verbosity may be due to issues in the ASP.NET MVC design we plan to improve.
Requesting Gravatar... StevenHarman.net Dec 12, 2007 9:54 PM
# TDD DI ASP.NET MVC Made Better with StructureMap's Fluent API, Oh My!
TDD DI ASP.NET MVC Made Better with StructureMap's Fluent API, Oh My!
Requesting Gravatar... nibblers revenge Dec 19, 2007 7:24 AM
# ASP.NET 3.5 Extensions CTP Preview Released
ASP.NET 3.5 Extensions CTP Preview Released
Requesting Gravatar... Developer Blogs Dec 21, 2007 7:42 AM
# ASP.NET 3.5 Extensions CTP Preview Released
Earlier today we released the first CTP preview of an &amp;quot;ASP.NET 3.5 Extensions&amp;quot; release that
Requesting Gravatar... My World Jan 03, 2008 10:14 PM
# TDD for ASP.NET MVC using StructureMap
Ok, so I just posted less than an hour ago, but now I've found something worth talking about. :) After
Requesting Gravatar... HUMAN-DEBUGGER.NET Jan 05, 2008 3:25 PM
# MVC (asp.net MVC Framework) - Controller Factory 2
Requesting Gravatar... Omid Zaman Jan 06, 2008 3:46 PM
#
ASP.NET MVC Is Available (CTP)
Requesting Gravatar... RainSunny Jan 09, 2008 8:54 PM
# ASP.NET 3.5 Extensions CTP Preview Released
Requesting Gravatar... HUMAN-DEBUGGER.NET Jan 14, 2008 11:55 AM
# MVC (asp.net MVC Framework) - Controller Factory
Requesting Gravatar... HUMAN-DEBUGGER.NET Jan 17, 2008 11:55 PM
# MVC (asp.net MVC Framework) - Controller Factory
Requesting Gravatar... ASP.NET Chinese Blogs Jan 18, 2008 10:16 AM
# ASP.NET 3.5 Extensions CTP预览版发布了
【原文地址】 ASP.NET 3.5 Extensions CTP Preview Released 【原文发表日期】 Sunday, December 09, 2007 8:55 PM 今天早些时候
Requesting Gravatar... Basharat Hussain Jan 20, 2008 11:09 PM
# TDD and Dependency Injection with ASP.NET MVC - error in compilation
Hi,
I could not compile the sample. I have following errors in config files. Please help me in this regard.
In StructureMap.config file - <StructureMap> says element is not declared
In Web.Config file - <dynamicData attribute gives following error "the element 'system.web.extensions' has invalid child dynamicData, List of possible elements expected: 'Scripting'"

Please guide me to solve these issues. thanks.

BR/Bash


Requesting Gravatar... Noticias externas Jan 23, 2008 1:38 AM
# ASP.NET ed i Patterns: ASP.NET MVC (Model, View, Controller) Framework (Parte 2)
Nella prima parte del post ho introdotto il nuovo ASP.NET MVC Framework fino all&amp;#39;implementazione
Requesting Gravatar... CodeClimber Jan 28, 2008 11:04 AM
# ASP.NET MVC Link collection
ASP.NET MVC Link collection

What do you have to say?

(will show your gravatar)
Please add 2 and 2 and type the answer here: