MVC: unit tests

Having spent all my working life in academia, I never got into the formal-testing-of-code mindset, even though I wrote some fairly hefty programs. Of course I tested my programs, but it was mainly a case of trying every possible scenario by hand and then fixing things that didn’t work.

Testing has turned into something of an industry in its own right. The more extreme testers advocate test-driven development, in which the test for a section of code is written before the code itself, and the code is then written so it passes the test.

Whatever your philosophy on testing, having a set of tests which can be run at any time at the press of a button is surely a nice idea, and Visual Studio has tools that support this form of testing. We’ll have an introductory look at how to include unit tests in an MVC4 project, using Visual Studio 2012 (version 11).

When you create an MVC4 project in VS, there is an option to create a test suite at the same time. However, if you don’t do this and opt instead for the empty project (as we’ve done up to now), it’s easy enough to add in a test project later.

One thing is worth pointing out here, though. Older versions of VS had a feature where you could right-click on a class or method in your code and add a unit test on the fly. For technical reasons, this feature has been removed in VS2012, so the only way of adding tests (as far as I can tell) is via the test project itself.

To add a test project to an existing solution, right-click on the existing solution in Solution Explorer and select Add–>New project, then click on the Visual C#–>Test node and select Unit Test Project. You’ll get a starter C# file which looks like this:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ComicShop.UnitTest
{
  [TestClass]
  public class UnitTest1
  {
    [TestMethod]
    public void TestMethod1()
    {
    }
  }
}

Note that a class used for unit testing must have the attribute TestClass, and a method must have the attribute TestMethod.

Even at this stage, we can run the test. In the Test menu, select Windows–>Test explorer, and in the Test Explorer window, click on Run All. Since there are no conditions yet that can fail, the test should pass, and you’ll see a message to this effect.

To see what happens when a test fails, insert the line Assert.IsTrue(1 > 2); in the TestMethod1 method and run the test again. The condition is clearly false, so the test will fail, and the Test Explorer tells you this, along with which test failed.

Since we’ll be testing code we’ve written in the original ComicShop project, the ComicShop.UnitTests project needs access to that code, which it doesn’t have at the moment. To rectify this, right-click on References in the ComicShop.UnitTests project and then Add reference. Expand the Solution node and under Projects, select the ComicShop project and add it.

We now have enough material to be able to add unit tests to our existing code. However, the code as it stands isn’t in quite the right form for ‘proper’ unit testing. The reason is that the controller code we wrote in earlier posts has a direct link with the database used to store the comic book data. Thus any unit test we write for a controller method will involve connecting to the database. You might think that any proper test would have to do this anyway; after all, the program connects to the database so surely any test would also have to do this.

The point is that there are really two distinct operations involved in the controller method as it stands. First, there is the connection to, and interaction with, the database. Second, there is the logic in the controlller method itself; that is, the logic that determines what the controller does with the data it gets from the database (or sends to it, if we’re adding a new comic book). We’d really like to test these two things separately.

We’ll see in the next post how to decouple the controller from the database, but for now, just to get used to writing a unit test, we can write a simple unit test with the controller code as it stands. As a reminder, here’s the code for the Index() method in the HomeController.

    private ComicContext database = new ComicContext("ComicContextDb");
    public ActionResult Index()
    {
      var comics = database.ComicBooks.Select(book => book).OrderBy(book => book.Title);
      var model = comics.ToList();
      return View("IndexModel", model);
    }

We’ll test that the ActionResult returned at the end isn’t null.

It’s a good idea to mirror the the original application’s code structure in the unit test code structure, so we’d like tests on HomeController to be in a Controllers namespace within ComicShop.UnitTests. To do this, right-click on the ComicShop.UnitTests project and select Add–>Add folder, then name this folder Controllers. Right-click on the Controllers folder and then on Add–>Unit test. Name the new test HomeControllerTests. (Do not click on Add–>Class to add the test, since the resulting class won’t be included in the tests.)

Here’s the complete class that does our simple test.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ComicShop.Controllers;
using System.Web.Mvc;

namespace ComicShop.UnitTests.Controllers
{
  [TestClass]
  public class HomeControllerTests
  {
    [TestMethod]
    public void Index_ValidView()
    {
      HomeController homeController = new HomeController();
      var result = homeController.Index();
      Assert.IsNotNull(result);
    }
  }
}

We create a HomeController object, call its Index() method and save the returned value in ‘result’. We then apply one of the static methods in the Assert class (one of the provided UnitTesting classes). As you can see from the Intellisense, there are a lot of tests in Assert. This one does just as it says: it tests if ‘result’ is null.

Before we leave this introductory post, it’s worth pointing out that any method that is to be used as a TestMethod must be void (return nothing) and have no parameters. The unit test must be completely encapsulated within the code for that method.

In order to test anything more significant than this, we should really restructure our code so that we decouple the controller from the database, but that’s a topic for the next post.

Advertisements
Post a comment or leave a trackback: Trackback URL.

Trackbacks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: