Monday, May 17, 2010

Testing MVC 2.0 Controllers with Moq & NUnit




So I’ve been learning more and more about how to properly test my controllers in my MVC projects and I thought I’d share. I started using Moq recently and really like the syntax. It comes more natural to me over Rhino Mocks, which is what I was using before. I also started taking advantage of the MvcContrib test helpers, which are really nice! Of course I’m using NUnit, which seems to be what just about everyone uses. On a side note, testing is one of the major reasons to switch over to MVC from web forms. If you’re not using MVC now, I highly recommend switching over. You will not be disappointed.

Alright, well I’m going to be writing the tests for the contact form I blogged about back in March. Let’s get started.

First let’s test the Index() for the ContactController.

    [TestFixture]
public class ContactControllerTests
{
[
Test]
public void Index_Should_Return_Type_Of_ContactView()
{
//Arrange
var result = new ContactController();
//Act
result.Index();
//Assert
Assert.That(result.ViewData.Model.GetType(), Is.EqualTo(typeof(ContactView)));
}
}

The general rule of thumb for a unit test is Arrange, Act, & Assert. There generally should only be one assert per test, although I don’t think it’s set in stone. I’m sure some people disagree with that statement. Anyhow, this test is simple enough. Create a new instance of the controller, call the Index() action and assert that the returned model type is of ContactView. Done.

So I start on the next test and realize I’m going to need to use the SetUp attribute because I’m going to have some recurring items. Okay, so that looks like this:

        private ContactController _contactController;
[
SetUp]
public void Setup()
{
_contactController =
new ContactController();
}

So the second test looks like this:

       [Test]
public void Send_Should_Return_ContactView_If_ModelState_Has_Error()
{
_contactController.ModelState.AddModelError(
"testerror", "test error");
_contactController.Send(
new ContactView());

Assert.That(_contactController.ViewData.Model.GetType(), Is.EqualTo(typeof(ContactView)));
}

Basically, this adds an error and calls the Send() action and finally we assert that the ContactView is passed back. Here’s where we could add another Assert that checks the IsValid on the modelstate too, but I didn’t.

Next test looks like this:

        [Test]
public void Send_Should_Call_NotificationService_If_ModelState_IsValid()
{
var notificationService = new Mock<INotificationService>();
ObjectFactory.Inject(typeof(INotificationService), notificationService.Object);
_contactController.Send(
new ContactView());

notificationService.Verify(service => service.Notify());
}

So I use Mock<> to mock the notification service and then the StructureMap ObjectFactory to inject the mock in and then call the Send() action. Finally, I assert that Notify() was called on the mock.

Next I test to make sure the Success is passed in. Looks like this:

        [Test]
public void Send_Should_Call_NotificationService_If_ModelState_IsValid_And_Return_Success()
{
_contactController.Send(
new ContactView());

Assert.That(_contactController.ViewData["Success"], Is.True);
}

Then I test if the exception is thrown and it looks like this:

        [Test]
public void Send_Should_Call_NotificationService_And_Throw_A_NotificationException_If_Send_Fails()
{
_notificationService.Setup(service => service.Notify()).Throws(
new NotificationException());
_contactController.Send(
new ContactView());

Assert.That(_contactController.ModelState.IsValid, Is.False);
}

Notice I had to put the notification service in the SetUp too. Now my setup looks like this:

        private Mock<INotificationService> _notificationService;
private ContactController _contactController;
[
SetUp]
public void Setup()
{
_notificationService =
new Mock<INotificationService>();
ObjectFactory.Inject(typeof(INotificationService), _notificationService.Object);

_contactController =
new ContactController();
}

Finally I test to make sure everything comes back okay and the ContactView is passed back too. Looks like this:

        [Test]
public void Send_Should_Return_ContactView_If_No_Errors_Occur()
{
_contactController.Send(
new ContactView());

Assert.That(_contactController.ViewData.Model.GetType(), Is.EqualTo(typeof(ContactView)));
}

I also refactored the IsValid ModelState test to this:

        [Test]
public void Send_Should_Call_NotificationService_If_ModelState_IsValid()
{
_contactController.Send(
new ContactView());

_notificationService.Verify(service => service.Notify());
}

All our tests complete and green…

image

Thanks for reading!

Download the Source Here.


Shout it

kick it on DotNetKicks.com

blog comments powered by Disqus
Related Posts Plugin for WordPress, Blogger...