Unit Testing Simple ASP.NET MVC Controllers

I have created enough simple projects using ASP.NET MVC with unit tests to notice a very helpful pattern. The following is a sample of a test fixture using RhinoMocks and NUnit to test a controller.

   1: [TestFixture]

   2: public class AdminControllerTests

   3: {

   4:     private IFacilityRepository facilityRepository;

   5:     private IMeetingRepository meetingRepository;

   6:     private IUserSession userSession;

   7:  

   8:     [SetUp]

   9:     public void SetUp()

  10:     {

  11:         facilityRepository = MockRepository.GenerateMock<IFacilityRepository>();

  12:         meetingRepository = MockRepository.GenerateMock<IMeetingRepository>();

  13:         userSession = MockRepository.GenerateMock<IUserSession>();

  14:     }

  15:  

  16:     [Test]

  17:     public void SaveMeeting_should_call_Add_on_MeetingRepository_if_MeetingId_is_zero()

  18:     {

  19:         // Arrange

  20:         var meetingData = new MeetingData { MeetingId = 0, FacilityId = 0 };

  21:         meetingRepository.Stub(x => x.GetById(0)).Return(new Meeting());

  22:         facilityRepository.Stub(x => x.GetById(0)).Return(new Facility());

  23:         var controller = GetController();

  24:  

  25:         // Act

  26:         controller.SaveMeeting(meetingData);

  27:  

  28:         // Assert

  29:         meetingRepository.AssertWasCalled(x => x.Add(Arg<Meeting>.Is.Anything));

  30:         meetingRepository.AssertWasNotCalled(x => x.Update(Arg<Meeting>.Is.Anything));

  31:     }

  32:  

  33:     [Test]

  34:     public void SaveMeeting_should_call_Update_on_MeetingRepository_if_MeetingId_is_not_zero()

  35:     {

  36:         // Arrange

  37:         var meetingData = new MeetingData { MeetingId = 1, FacilityId = 1 };

  38:         meetingRepository.Stub(x => x.GetById(1)).Return(new Meeting());

  39:         facilityRepository.Stub(x => x.GetById(1)).Return(new Facility());

  40:         var controller = GetController();

  41:  

  42:         // Act

  43:         controller.SaveMeeting(meetingData);

  44:  

  45:         // Assert

  46:         meetingRepository.AssertWasNotCalled(x => x.Add(Arg<Meeting>.Is.Anything));

  47:         meetingRepository.AssertWasCalled(x => x.Update(Arg<Meeting>.Is.Anything));

  48:     }

  49:  

  50:     private AdminController GetController()

  51:     {

  52:         return new AdminController(userSession, meetingRepository, facilityRepository);

  53:     }

  54: }

 

The reason I’m being explicit about the term “simple” in this case is that the controller above that I’m testing doesn’t have much going on. It has some constructor dependencies (like all controllers with dependencies should), but that’s about it. The gist of the pattern is made up of three things:

  1. A SetUp method, via NUnit, runs before each test to create new, fake implementations of the dependencies. In this case I’m using RhinoMocks, but you’re not limited to that (see below). Declaring them at the class level is nice, it allows you to use or ignore them at your leisure.
  2. Creating the GetController() method constrains the creation of a controller to one place. This is good because your dependencies can change by adding more or removing existing dependencies. By creating one place to “new up” the controller, you only have to update one area when dependencies change instead of updating a constructor in each test. This isn’t anything new for unit testing, just a good practice.
  3. Finally, your individual tests can get a new controller and stub/mock/fake the methods of your dependencies at any time during the test method and assert values or verify behavior at any time during the test.

This pattern has helped me a lot; I wish I had this in mind when I was writing lots of unit tests for controllers a year and a half ago (so I didn’t have to go back and change some of them later).

Another isolation/mocking framework I like to use is Moq. For the above example, there is a slight difference in the way the same fixture is used. The following is the same tests using Moq.

   1: [TestFixture]

   2: public class AdminControllerTests

   3: {

   4:     private Mock<IFacilityRepository> facilityRepository;

   5:     private Mock<IMeetingRepository> meetingRepository;

   6:     private Mock<IUserSession> userSession;

   7:  

   8:     [SetUp]

   9:     public void SetUp()

  10:     {

  11:         facilityRepository = new Mock<IFacilityRepository>();

  12:         meetingRepository = new Mock<IMeetingRepository>();

  13:         userSession = new Mock<IUserSession>();

  14:     }

  15:  

  16:     [Test]

  17:     public void SaveMeeting_should_call_Add_on_MeetingRepository_if_MeetingId_is_zero()

  18:     {

  19:         // Arrange

  20:         var meetingData = new MeetingData { MeetingId = 0, FacilityId = 0 };

  21:         meetingRepository.Setup(x => x.GetById(0)).Returns(new Meeting());

  22:         facilityRepository.Setup(x => x.GetById(0)).Returns(new Facility());

  23:         var controller = GetController();

  24:  

  25:         // Act

  26:         controller.SaveMeeting(meetingData);

  27:  

  28:         // Assert

  29:         meetingRepository.Verify(x => x.Add(It.IsAny<Meeting>()));

  30:         meetingRepository.Verify(x => x.Update(It.IsAny<Meeting>(), Times.Never()));

  31:     }

  32:  

  33:     [Test]

  34:     public void SaveMeeting_should_call_Update_on_MeetingRepository_if_MeetingId_is_not_zero()

  35:     {

  36:         // Arrange

  37:         var meetingData = new MeetingData { MeetingId = 1, FacilityId = 1 };

  38:         meetingRepository.Setup(x => x.GetById(1)).Returns(new Meeting());

  39:         facilityRepository.Setup(x => x.GetById(1)).Returns(new Facility());

  40:         var controller = GetController();

  41:  

  42:         // Act

  43:         controller.SaveMeeting(meetingData);

  44:  

  45:         // Assert

  46:         meetingRepository.Verify(x => x.Add(It.IsAny<Meeting>(), Times.Never()));

  47:         meetingRepository.Verify(x => x.Update(It.IsAny<Meeting>()));

  48:     }

  49:  

  50:     private AdminController GetController()

  51:     {

  52:         return new AdminController(userSession.Object, meetingRepository.Object, facilityRepository.Object);

  53:     }

  54: }

Both of these examples are using the Arrange/Act/Assert (AAA) syntax. The idea is that you set up your context, run some code, then verify the results of your objects and/or system under test.

This has been a very good to me and a simple pattern for testing most controllers in many of the simple MVC applications I’ve worked.

Related:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Chris Missal

Oh hey, I'm a Senior Consultant for Headspring in Austin, TX. I've been working in software professionally since 2006 and I really, really love it. I'm mostly in the Microsoft world, but enjoy building computer things of all sorts (to be vague). When I'm not slinging code, I'm probably out and about slinging discs, bowling balls, or good beer with great friends.
This entry was posted in ASP.NET MVC, Development, DRY, Moq, NUnit, RhinoMocks. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.arrangeactassert.com Jag Reehal

    Hi Chris,

    Yep behavior tests can be used for ASP.NET MVC Controllers and I especially like the AAA syntax :)

    I did a similar post and covered other controller unit tests such as returning the correct view, and the Post-Redirect-Get pattern.

    See http://www.arrangeactassert.com/how-to-unit-test-asp-net-mvc-controllers/

  • http://geekswithblogs.net/wesm Wes

    This type of controller approach tends to work as a service layer in an application (when concerns are properly separated). In this case the controller is bringing together repositories and a user session which are components of the domain.

    Mock based testing of service layers is often tedious and fragile as the tests duplicate the code in the service layer, any change to that code requires a change to the tests to make them pass. I constantly ran into this problem when mocking service layers or worse when I left domain logic in service layers.

    Time has shown that focusing on invariants (state based testing), is the best way, at least for me, to do testing. In the case of a service layer, the invariants revolve around integrating components and as such testing should be from the integration perspective instead of a unit. If I find myself excessively mocking/stubbing in a service layer, it is often a smell that I have domain logic to extract (fat service calls). This helps maximize the testing surface of domain logic.

    I usually work with a smoke test of the integration components to avoid duplicating the unit tests of the components it brings together. This allows the underlying component architecture to change, so long as the service still fulfills the same invariant.

    In this example, a good smoke test for a controller action to add an item might be to check if it exists in the repository after the call. The repository would be stubbed instead of mocked, with an in memory version or a real database.

    Another problem that frequently occurs with mock testing is the ignoring of argument values. These are important if our tests solely rely on the interaction of components instead of the invariants. In the example above, any Meeting object could have been added to the repository and the test would pass.

    Just some thoughts on the pain points we’ve experienced with different testing strategies.

  • Sukant Hajra

    the way you’re doing Arrange-Act-Assert is pretty much the same as how I’ve been doing Given-When-Then in unit tests (without BDD-framework bloat)

  • http://khebbie.dk Klaus Hebsgaard

    I have found that using the testdata builder pattern works great for controllers.
    I would create the controller in a test data builder class, in a fluent like style, like:
    var controller = new AdminControllerTestDataBuilder()
    .WithSimpleFacilityrepository()
    .WithMeetingRepository()
    .withXYZ()
    .Build();

    And then I could insert instances where needed using :
    WithFacilityrepository(facilityRepository.Object)

  • MVC developr

    Hey, i found this after some digging around
    http://kbochevski.blogspot.com/2010/06/unit-testing-mvcnet.html
    My colleagues found this very helpful too. It covers all the layers with unit testing and uses Rhino and MVC contrib, The source code is a good start for beginners like me

  • http://www.chitownbootcamps.com/389/lakeview-personal-trainer/ Lakeview Personal Trainer

    Good news.This is a great post. I like this topic.This site has lots of advantage.
    I found many interesting things from this site. It helps me many away.