Test Driven Development with ASP.NET MVC
(
Sep 16 2008 - 08:05:02 PM by
Timothy Khouri) - [
print article]
One of the biggest benefits of MVC is it's direct link to Test Driven Development. Because of some of the new features of ASP.NET MVC Preview 5 (ModelBinders in particular), testing your Action methods is even easier. This article will demonstrate how easy it is to ensure the quality of your MVC app with unit tests.
For those of you who are not familiar with Test Driven Development (TDD) or it's benefits, consider the following scenario. Let's assume that you have been contracted to build a site that deals with customer management. One of the client's business rules is that customers must have at least a first name, last name and a zip code. This is a fairly straight forward request that will help us easily get into TDD.
Creating MVC Apps With Unit Tests
To prove that MVC is meant for testing, the Visual Studio template for a new ASP.NET MVC (Preview 5) project will automatically ask if you want to include a Unit Test project as well.
The default Test Project that comes with the ASP.NET MVC Visual Studio template is pretty basic, and includes a test class for each of the default Controller's that come with your MVC app.
So, the steps to start our project are very simple:
- File -> New -> Project
- ASP.NET MVC Web Application
- Yes, create a unit test project.
Business Rules as Tests
So, after learning what our business rules are, our next step in TDD is to express those business rules in the form of unit tests. To do this, we'll add a class to our Test Project that will handle testing all things "Customers" related. We'll need to decorate the class with the "TestClass" attribute, and each method with the "TestMethod" attribute. Our "customers test class" should look like this:
[TestClass]
public class TestCustomersController
{
[TestMethod]
public void EnsureRequiredFields()
{
}
}
Now that we have a stub for our tests, we need to do a *little* bit of implementing our code in our CustomersController class in our ASP.NET MVC app. All we are going to do is create the method stub that is responsible for creating new customers. Once we do that, we can come back and finish our test method.
I mentioned earlier in the article that using a ModelBinder will make writing tests easier, and that may seem confusing at first. ModelBinders are run by the ASP.NET MVC framework, and will not be used by our Test Project, so how can they help in writing unit tests?
The answer is simple: Object Oriented Programming. Basically, because ModelBinders allow me to write my ASP.NET MVC Action Methods with complex objects as parameters, I can test against them in a much more OO fashion. Example:
public ActionResult Create()
{
if (string.IsNullOrEmpty(Request["firstName"]))
{
}
}
public ActionResult Create(Customer newCustomer)
{
if (string.IsNullOrEmpty(newCustomer.FirstName))
{
}
}
So now we are ready to write our tests, but first we should mention a little "gotcha" that needs to be addressed. Because I only want to test my business rules, and not the code that ultimately writes the customer record to the database, I need to prevent my code from actually running the "store in database" part of my code. This is known as "mocking".
Testing One Step at a Time
There are three pieces to our business rule that we are going to test individually: Required first name, last name and zip. To do this, we're going to create an instance of our CustomersController, and call Create method. We're going to pass in a customer object that *should fail validation*, and then we'll check to make sure that, in fact, the Create method did fail.
This may seem odd (to hope for failure), but again remember that we want to test to make sure that validation is working, so we have to pass in bad data and look for the validation to come back.
Here's our code that we'll put inside of our "EnsureRequiredFields" method mentioned earlier in the article:
Customer newCustomer;
CustomersController controller = new CustomersController();
MockManager.Mock(typeof(DataStore)).ExpectAlways("InsertCustomer");
{ // Test for required "FirstName".
controller.ViewData.ModelState.Clear();
newCustomer = new Customer
{
FirstName = "",
LastName = "Smith",
Zip = "34275",
};
controller.Create(newCustomer);
Assert.IsTrue(controller.ViewData.ModelState.Count == 1, "FirstName must be required.");
}
Now, when we run our tests in Visual Studio we will fail until we code the appropriate validation in our Create method. Here's what that will look like:
Now you keep coding your app until you pass that test (which in this case should be pretty easy). So, once we add this code to our Create method, we'll be good to go:
if (string.IsNullOrEmpty(newCustomer.FirstName))
{
this.ViewData.ModelState.AddModelError("FirstName", "", "'FirstName' is a required field.");
isValid = false;
}
if (isValid == false)
{
return View();
}
Conclusion
Hopefully this article has been able to demonstrate how easy it is to start down the path of TDD with the ASP.NET MVC framework. The demo project for this article can be downloaded here: MvcDemo_Testing_CreateCustomer.zip.