Live Demos

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

5 comments:

  1. Now I am going away to do my breakfast, once having my breakfast coming again to
    read more news.

    Look into my weblog - double coated dogs

    ReplyDelete
  2. At this time it seems like Expression Engine is the top blogging platform available right
    now. (from what I've read) Is that what you are using on your blog?

    Feel free to visit my web site ... garbage disposal troubleshooting

    ReplyDelete
  3. I could not resist commenting. Very well written!

    Also visit my web page; baseball training

    ReplyDelete
  4. Hi! I've been following your blog for some time now and finally got the courage to go ahead and give you a shout out from Lubbock Tx! Just wanted to tell you keep up the excellent work!

    Here is my site - 2006 honda s2000 for sale

    ReplyDelete
  5. Amazing blog! Is your theme custom made or did you download it from somewhere?
    A design like yours with a few simple adjustements would really make my blog
    jump out. Please let me know where you got your design.
    With thanks

    Review my weblog; wood bats

    ReplyDelete