The Ultimate Guide To Unit Testing in ASP.NET MVC

January 27th, 2016

With most development, you need checks-and-balances with your application. Today, I show you what makes unit testing great and the best ways to achieve exceptional unit test coverage in your ASP.NET MVC application.

Unit tests are definitely becoming a need-to-have skill for developers to confirm their code does what it should for production.

If you aren't building unit tests for your code, I would suggest looking into unit test libraries and frameworks along with a mocking framework to intercept external calls to other resources.

I used to be one of the developers who poo-poo'd unit tests, but now, I can't live without them. They're a way to perform checks-and-balances on your code.

Personally, there have been two instances that have changed my way of thinking with unit tests:

As you can see, these unit tests solve a number of issues in their own way.

Let's get into the basics of different types of testing.

Terminology

The basics of testing can be broken into the following categories:

With those terms explained, the unit tests and integration tests are the most important for this particular post, but trust me, there are a ton of different software testing methods!

Everyone, and I mean everyone, in the IT industry is testing the quality of their products. From the first line of code a developer writes to the last power user giving their stamp-of-approval before it heads out the door...

...Everyone is a part of QA now (in one way or another)!

What are some general guidelines for Unit Testing?

Based on my experiences, here are some of my guidelines I've learned over the years.

Of course, unit testing can be specific to individuals or even a company culture.

Does anyone feel the same way? Post your comments below. Give me your thoughts.

So How Does Unit Testing Work in ASP.NET MVC?

This was one of the selling points with ASP.NET MVC for me. The structure of an MVC project is already testable right out of the box.

Once you start Visual Studio and create your first ASP.NET MVC project, you can start writing unit tests before you write any code.

Usually, you create a separate unit test project for each project.

I follow the Given/When/Then method of writing unit tests where:

You'll see some examples below when we get to each MVC term.

How Do You Unit Test HttpContext and the Database?

Ahh the age old question about HttpContext...How can you mock HttpContext?

Mocking an HttpContext can be difficult to mock because you are expecting a certain result from server variables or referring urls. How do you test against an environment-based object?

Luckily, I already mentioned this in a previous post called How to Successfully Mock HttpContext that describes all of the ways to return relevant HTTP data back in your unit tests.

As for the database, I was able to come up with a way to mock a database relatively quick.

Finally, The ASP.NET MVC Unit Tests

Since there's a lot of technology "hooks" in ASP.NET MVC and I want to go over all of them with their corresponding unit tests.

Now let's get into some MVC unit testing.

Controllers

Controllers are the workhorses of MVC. The behavior we want to test with controllers are the action results or view model returned from them.

GivenAnAboutController\WhenRequestingTheAboutPage.cs

[TestClass]
public class WhenRequestingTheAboutPage
{
    [TestMethod]
    public void ThenReturnTheAboutViewModel()
    {
        // Arrange
        var controller = new AboutController();
 
        // Act 
        // No database calls in the Index() method.
        // Just setting the title.
        var result = controller.Index() as ViewResult;
        var model = result.Model as AboutViewModel;
 
        // Assert
        Assert.IsNotNull(result);
        Assert.AreEqual("About DanylkoWeb", model.Title);
    }
}

This is the simplest you can get from a controller. You pass a model into the View and it replaces what it needs to on the View.

But we all know that most controllers aren't this simple. We are accessing a database or something inside the controller, right?

Something like this:

public class FaqController : Controller
{
    private readonly IFaqRepository _repository;
    public FaqController(): this(new FaqRepository()) { }
 
    public FaqController(IFaqRepository repository)
    {
        _repository = repository;
    }
 
    public ActionResult Index()
    {
        var records = _repository.GetAll();
 
        var model = new FaqViewModel
        {
            FAQs = records
        };
 
        return View(model);
    }
}

We need to mock up a FaqRepository. So how do we do that? Using a mock framework. I used to use RhinoMocks, but switched over to Moq.

Here is how we would mock up a FaqRepository.

[TestMethod]
public void ThenReturnTheFaqViewModel()
{
    // Arrange
    var faqs = new List<Faq>
    {
        new Faq {Id = "1", Answer = "Home", Question = "Where Do you Live?"},
        new Faq {Id = "2", Answer = "Since I was 11", Question = "When did you start programming?"},
        new Faq {Id = "3", Answer = "In Pennsylvania", Question = "Where were you born?"}
    };
    var faqRepository = new Mock<IFaqRepository>();
    faqRepository.Setup(e => e.GetAll()).Returns(faqs.AsQueryable());
    var controller = new FaqController(faqRepository.Object);
 
    // Act 
    var result = controller.Index() as ViewResult;
    var model = result.Model as FaqViewModel;
 
    // Assert
    Assert.IsNotNull(result);
    Assert.AreEqual(3, model.FAQs.Count());
}

Whatever is passed into your controller or new'ed up in your controller that accesses data, make sure you can mock it up (not MUCK it up) and pass it in.

Action Filters

ActionFilters are attributes on each action method in a controller. This is where we experience HttpContext occurrences so we need a mocked up HttpContext, which can be found here.

Let's look at the SearchBotFilter we wrote a while back as an example.

public class SearchBotFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.HttpContext.Request.Browser.Crawler)
        {
            filterContext.Result = new ViewResult() { ViewName = "NotFound" };
        }
    }
}

This isn't that intimidating. We just need to confirm that the value returned is the right ViewName.

[TestMethod]
public void ThenConfirmAuthorizationActionFilterWorkedAsExpected()
{
    // Arrange
    var moqContext = new Mock<HttpContextBase>();
    var moqRequest = new Mock<HttpRequestBase>();
    moqContext.Setup(x => x.Request).Returns(moqRequest.Object);
    moqContext.Setup(x => x.Request.Browser.Crawler).Returns(true);
 
    var raa = new SearchBotFilter();
    var filterContextMock = new Mock<ActionExecutedContext>();
    var viewResultMock = new Mock<ViewResultBase>();
    filterContextMock.Object.Result = viewResultMock.Object;
    filterContextMock.Setup(e => e.HttpContext).Returns(moqContext.Object);
 
    // Act
    raa.OnActionExecuting(filterContextMock.Object);
    var viewResult = filterContextMock.Object.Result as ViewResult;
 
    // Assert
    Assert.IsTrue(viewResult.ViewName == "NotFound");
 
}

Action Results

Action Results are used to return the proper data to a View. The expected behavior for ActionResults is to determine the proper model sent to the View.

[TestMethod]
public void AboutIndexTest()
{
    // Arrange
    var controller = new AboutController();
 
    // Act
    var result = controller.Index();
 
    // Assert
    Assert.IsInstanceOfType(result, typeof(ViewResult));
    var viewResult = result as ViewResult;
    Assert.AreEqual(typeof(AboutViewModel), viewResult); }

You can optionally check that the proper model is returned as well.

Model Binders

If you are posting data back to an action method in a controller and the data in the form requires a specific model (i.e. Uploading a file), you can easily use ModelBinders to package everything into a nice model to pass it into a controller. The behavior expected is a model passed into the controller.

I posted about unit testing ASP.NET MVC model binders in a past post so I defer you to that post. :-)

UrlHelpers

UrlHelpers are great for cataloging your web application with Urls. We expect a string returned based on parameters passed into the UrlHelper.

There is one problem with UrlHelpers...they take a lot of setup to retrieve the Url to properly test.

Let me show you what I mean.

[TestMethod]
public void ThenVerifyTheRightLoginUrlIsReturned()
{
    // Arrange
    var routes = new RouteCollection();
    RouteTable.Routes.Clear();
 
    var request = new Mock<HttpRequestBase>(MockBehavior.Strict);
    request.SetupGet(x => x.ApplicationPath).Returns("/");
    request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/login"UriKind.Absolute));
    request.SetupGet(x => x.ServerVariables).Returns(new NameValueCollection());
 
    var response = new Mock<HttpResponseBase>(MockBehavior.Strict);
    response.Setup(x => x.ApplyAppPathModifier("/login")).Returns("http://localhost/login");
 
    var context = new Mock<HttpContextBase>(MockBehavior.Strict);
    context.SetupGet(x => x.Request).Returns(request.Object);
    context.SetupGet(x => x.Response).Returns(response.Object);
 
    var controller = new LoginController();
    controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
    controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), routes);
 
    // Act
    var login = controller.Url.LoginUrl();
 
    // Assert
    Assert.AreEqual("/Login", login);
}

See what I mean? It would be easier to create an TestInitialize at the top containing all of this plumbing and just issue it once.

HtmlHelpers

HtmlHelpers are used for returning properly-formatted HTML snippets of code. The behavior we are expecting back is properly-formatted HTML based on criteria passed into the helper.

HtmlHelpers are no better. They also take a lot of set up to unit test properly. I would also like to thank Thomas Ardel for his HtmlHelper routine. Awesome work, sir! :-)

[TestMethod]
public void ThenVerifyTheRightLoginUrlIsReturned()
{
    // Arrange
    var faq = new Faq
    {
        Id = "2",
        Answer = "Home",
        Question = "Where do you live?"
    };
    HtmlHelper helper = CreateHtmlHelper<Faq>(
        new ViewDataDictionary(faq));
    
 
    // Act
    var result = helper.EditFAQItem(faq);
 
    // Assert
    Assert.AreEqual("<a href=\"#2\">Faq Item #2</a>", result);
}
public HtmlHelper<T> CreateHtmlHelper<T>(ViewDataDictionary viewData) {     var cc = new Mock<ControllerContext>(         new Mock<HttpContextBase>().Object,         new RouteData(),         new Mock<ControllerBase>().Object);     var mockViewContext = new Mock<ViewContext>(         cc.Object,         new Mock<IView>().Object,         viewData,         new TempDataDictionary(),         TextWriter.Null);     var mockViewDataContainer = new Mock<IViewDataContainer>();     mockViewDataContainer.Setup(v => v.ViewData).Returns(viewData);     return new HtmlHelper<T>(         mockViewContext.Object, mockViewDataContainer.Object); }

A lot of work is required, but it can be tested!

Routes

Routing tells MVC which controller to use when a Url is entered. The expected behavior of a route is to know the controller, action, and optional id for when a Url comes in.

I discussed the routing unit testing in the post called How to Successfully Mock HttpContext (Yes, the same one from above).

The routing is pretty simple. 

Repository and Unit of Work (with Entity Framework)

The Repository and Unit of Work design patterns are great for accessing your databases. So how do you test the database without connecting to it?

The first thing you need is to create each of your mocking components from the lowest to the highest.

Our first thing we need is the "database." When I say database, I mean an in-memory table that provides the functionality we need for our unit test to pass. Of course, there are quicker ways to mock up a database.

For now, we'll use a simple in-Memory database using a mocked-up DbContext.

You may be wondering why we created a "fake" DbContext. The whole idea is to create a fake DbContext so we can test the Repository pattern and Unit of Work. That's the goal for mocking up a DbContext.

[TestMethod]
public void ThenReturnAllMenuItemsTest()
{
    // Arrange
    
    // __context is a Mocked DbContext created with Moq
 
    // Create our repository by passing in our DbContext.
    var mockMenuRepository = new Mock<MenuRepository>(_context.Object);
 
    // Mock unit of work has a lot of repositories in it.
    //  We pass the context into the Unit of Work.
    var mockUnitOfWork = new Mock<BaseUnitOfWork>(_context.Object);
    
    // If we call our MenuRepository in our unit of work, we return a mocked up MenuRepository.
    mockUnitOfWork.Setup(e => e.MenuRepository).Returns(mockMenuRepository.Object);
 
    // Act
    var menuItems = mockUnitOfWork.Object.MenuRepository.GetAll();
 
    // Assert
    Assert.AreEqual(5, menuItems.Count());
}

By setting up the three items to make a complete database call (DbContext, Repository, and Unit of Work), you can create an in-memory database of records to test any scenario that arises in a production environment.

Conclusion

After everything we covered in this post, there are always more ways to add unit tests for additional code coverage.

The great thing about ASP.NET MVC is that everything in the framework is unit testable. Of course, you can paint yourself into a corner if your design isn't completely flushed out and well understood.

If I missed any ASP.NET MVC technology that wasn't covered here, post a comment so we can get it into this Ultimate Guide to Unit Testing in ASP.NET MVC.

Post your comments below and Share this if you like it!