Exploring the ASP.NET MVC Preview 2 – Tests and Mocks
The changes made around testing and mocking are a big step in the right direction from Preview 1.
Mocking Controller Tests
In Preview 1, it was more common to create subclass testers for each Controller object—a very time consuming tasks. Mocks “worked”—but, honest, just acted a bit odd. Our ViewEngine still requires faking out, but Moq helps us along with the rest.
To start off, we’ll “Moq” up a Contact view and get the test to fail, then fix it (aka: implement it) to pass the test.
public void Contact_ReturnsContactView()
// Create a Moq instance of our HomeController.
var controller = new Mock<HomeController>();
// Create an instance of our FakeViewEngine.
// There has got to be a way to do this without
// ‘faking’ it.😦
var fakeViewEngine = new ViewHelper.FakeViewEngine();
// Set the ViewEngine of our mock object to the fakeViewEngine.
controller.Object.ViewEngine = fakeViewEngine;
// Using the extension method from the MvcMockHelpers class,
// set the controller context.
// Invoke the “Index” method.
// Assert that Index() actually returned a view named Index.
This is part of the unit testing I still don’t like—that FakeViewEngine class. The class consists of a ViewContext and get/set properties because, by default, IViewEngine can only RenderData()—it doesn’t allow direct access to the ViewContext.
I suppose you could override IViewEngine and implement your own—but, for testability, is there harm in exposing those as read-only?
At this point, our Contact() method call is invalid (as it doesn’t exist in the HomeController class.
For now, so our test will run, we’ll add in an empty method to the HomeController class.
public void Contact()
Now, we can run the test. As expected, it fails because the object (the ViewPage) doesn’t exist yet.
Test method Contact_ReturnsContactView threw exception: System.NullReferenceException: Object reference not set to an instance of an object..
Now, to set off to resolve the error.
Note: The templates are very precise. This is a content page (linked to a master page) and a view page rendering the view of a MVC controller. Be sure to pick the right item template for your task.
In our Views > Home directory, we need to add a new MVC View Content Page item named Contact.
After the View page itself has been added, implement the RenderView method in the HomeController class by modifying the empty method added earlier.
public void Contact()
Now, rerun our test!
There isn’t any mocking (yet) in our Route tests; however, we do use some of the helper methods Scott Hanselman wrote about and that I prepackaged up (see First Glance post for downloads). I’ve created two quick and simple tests:
- Does RegisterRoutes work successfully register routes to the RouteTable?
- Does the Default.aspx mapping work at the root?
Before we get started, the tests require a simple TestInitialize (or SetUp, depending on your testing tool) to initialize the RouteCollection object.
private RouteCollection routes;
public void TestInitialize()
routes = new RouteCollection();
Our RegisterRoutes test method is extremely easy:
public void RoutesRegistered()
Assert.IsTrue(routes.Count == 0);
Assert.IsTrue(routes.Count > 0);
Simply initialized, our RouteCollection should be empty (Count == 0) and, after RegisterRoutes is called, should be populated with routes. Easy enough and ensures that our RegisterRoutes method is doing it’s job.
To test our routes, we must do two things: register our route and then fake the navigation to a specific URL—in this example, “~/Contact.aspx”. This is really useful during transitions from other frameworks to MVC—especially if you have preprinted letterhead, business cards, etc. with a specific URL on it.🙂
To bypass the default “controller/action/id” logic of the MVC framework, we can specify exact names, paths, etc.
First, let’s build our test.
public void ContactRoot_MapsToHomeView()
RouteData routeData =
// Check to see if a route exists for the specified URL.
// Check to see that the controller matches our expectation.
// Check to see that the action matches our expectation.
This test checks three things to pass:
- does the URL specified exist in the route data (is there a valid route?),
- does it call the anticipated controller,
- and does it call the anticipated action?
All three are required to pass as a “valid route”.
When we try to run the test, the first to fail is our Assert.IsNotNull—a route does not exist for our Default.aspx page. So let’s add a route to get this test to pass.
routes.Add(new Route(“Contact.aspx”, defaultRouteHandler)
controller = “Home”,
action = “Contact”,
id = “”
Working with tests for both Controllers and Routes is quite a bit easier now—both due to continued improvement to the MVC framework and the work in the community with helper and extension methods.
For more details on MVC Testing, check out Scott Hanselman’s great webcast on asp.net.
Edit: Don’t bother with the asp.net web site with IE 8–-it’s totally borked up and, even worse, Silverlight won’t load properly in FireFox 2 (it’s an invalid browser). Break out IE7 for this venture.