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: }

</div> </div>

 

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: }

</div> </div>

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.

How I Approach a Defect