Test ASP.NET MVC routes using MVC Contrib

Last time I showed one alternative way of testing routes using Moq and NUnit. It works well but it adds code bloat. The MVC Contrib project has a nice set of test helper methods which simplifies it.

It assumes you use NUnit and RhinoMocks. It also assumes you set your routes in the ASP.NET MVC RouteCollection object. If you reference your routes in your test project like I do, then this shouldn't be a big issue either.

Our previous test method looked like this:


[Test]
public void Test_If_Product_Route_Returns_Expected_Result()
{
RouteCollection routes = new RouteCollection();
POC_Stuff.MvcApplication.RegisterRoutes(routes);

var context = MvcMockHelpers.FakeHttpContext("~/products/videogames/nintendo");
RouteData routeData = routes.GetRouteData(context);

Assert.That(routeData.Values["category"].ToString() == "videogames");
Assert.That(routeData.Values["subcategory"].ToString() == "nintendo");
Assert.That(routeData.Values["controller"].ToString() == "Catalog");
}


The new test method using the MVC Contrib test helpers will look like this:

[Test]
public void Test_If_Product_Route_Returns_Expected_Result()
{
"~/products/videogames/nintendo".ShouldMapTo(x => x.Index("videogames", "nintendo"));
}


I don't think you can make it much more compact! It will even do the assertions for you, the test helper will assert the route data, the controller, the method and its parameters. If you want to make additional asserts, that is possible as well since it returns a RouteData object from the System.Web.Routing namespace.

The main test class in the MVC Contrib project is called RouteTestingExtensionsand the method that will assert the route for us is called ShouldMapTo and the code as of when this post was written looks like this:


///
/// Asserts that the route matches the expression specified. Checks controller, action, and any method arguments
/// into the action as route values.
///

/// The controller.
/// The routeData to check
/// The action to call on TController.
public static RouteData ShouldMapTo(this RouteData routeData, Expression> action)
where TController : Controller
{
routeData.ShouldNotBeNull("The URL did not match any route");

//check controller
routeData.ShouldMapTo();

//check action
var methodCall = (MethodCallExpression) action.Body;
string actualAction = routeData.Values.GetValue("action").ToString();
string expectedAction = methodCall.Method.Name;
actualAction.AssertSameStringAs(expectedAction);

//check parameters
for (int i = 0; i < methodCall.Arguments.Count; i++)
{
string name = methodCall.Method.GetParameters()[i].Name;
object value = null;

switch ( methodCall.Arguments[ i ].NodeType )
{
case ExpressionType.Constant:
value = ( (ConstantExpression)methodCall.Arguments[ i ] ).Value;
break;
case ExpressionType.MemberAccess:
value = Expression.Lambda(methodCall.Arguments[ i ]).Compile().DynamicInvoke();
break;
}
value = (value == null ? value : value.ToString());
routeData.Values.GetValue(name).ShouldEqual(value,"Value for parameter did not match");


These doesn't seem to be too many developers in the community talking about this great test helper but I hope you see the value it adds.

  1. gravatar

    # by Dennis - July 21, 2009 at 1:17 AM

    This page doesn't display in a readable format for me in Firefox. Using version 3.0.11 on Windows Vista x64.

  2. gravatar

    # by Magnus Lassi - July 21, 2009 at 6:44 AM

    I haven't heard of that before, is the layout messed up or are you seeing page errors? I'm using Firefox 3.5.1 and Vista x32.

  3. gravatar

    # by Dennis - July 21, 2009 at 10:05 AM

    Here's a screen shot of what it looks like: http://picasaweb.google.com/lh/photo/tySq9FgwvYmx0wtOXAlQ5g?feat=directlink

  4. gravatar

    # by Dennis - July 21, 2009 at 12:40 PM

    OK, now a real question.

    "It also assumes you set your routes in the ASP.NET MVC RouteCollection object."

    How do I confirm that I'm doing that? I'm using routes.MapRoute within MvcApplication.RegisterRoutes method in the Global.asax code behind. Is there something else to do to set this up properly?

    All my routes are failing. Even the ones I know work when the app runs.

  5. gravatar

    # by Dennis - July 21, 2009 at 5:17 PM

    I got an answer on stackoverflow. I needed to call MvcApplication.RegisterRoutes(RouteTable.Routes)

  6. gravatar

    # by Magnus Lassi - July 21, 2009 at 6:32 PM

    Glad you found the issue. Correct, if you use the default template when you create your MVC project, you define your routes in the RouteCollection in global.asax via RegisterRoutes(RouteTable.Routes) .

    If you have problem getting the routes to work, I recommend Phil Haack's Route Debugger http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx . You just reference the dll and add the statement RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes) where you define your routes and you will see graphically which routes it found and which route it would use.