Sunday, April 25, 2010

Testing MVC Routes in C# and Moq

To enable you to test the custom routes that you have set-up firstly create a new RouteManager class.
public static class RouteManager
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
"CustomParameter",
"ControllerName/{action}/{Param1}/{Param2}",
new { controller = "ControllerName", action = "ActionName", Param1 = "", Param2 = "" }
);

routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);

}
}

You can then refactor your Global.asax.cs file to use the RouteManager:
protected void Application_Start()
{
RouteManager.RegisterRoutes(RouteTable.Routes);
}

With this set-up it is easy to test your MVC routes. The following code is an example of a simple routing test: ~

private static RouteData GetRouteData(string context)
{
var routes = new RouteCollection();
var mockOfHttpContext = new Mock();
mockOfHttpContext.Setup(httpContext => httpContext.Request.AppRelativeCurrentExecutionFilePath)
.Returns(context);
RouteManager.RegisterRoutes(routes);
return routes.GetRouteData(mockOfHttpContext.Object);
}

private static RouteData CheckMapping(String controller, String action, String suffix)
{
var context = "~/" + controller + "/" + action + "/" + suffix;
var routeData = GetRouteData(context);
Assert.AreEqual(controller, routeData.Values["controller"]);
Assert.AreEqual(action, routeData.Values["action"]);
return routeData;
}

[TestMethod]
public void MapsToDefaultRoute()
{
var expectedValue = "ParameterValue";
var routeData = CheckMapping("Controller", "ActionName", expectedValue);
Assert.AreEqual(expectedValue, routeData.Values["id"]);
}

[TestMethod]
public void MapsToCustomRoute()
{
var expectedValueForParam1 = "ParameterValue";
var expectedValueForParam2 = "ParameterValue";
var routeParameters = expectedValueForParam1 + "/" + expectedValueForParam2
var routeData = CheckMapping("ControllerName", "ActionName", routeParameters);
Assert.AreEqual(expectedValueForParam1, routeData.Values["Param1"]);
Assert.AreEqual(expectedValueForParam2, routeData.Values["Param2"]);
}

Mocking an MVC Controller in C# and Moq

When unit testing an MVC controller you will on occasions be required to mock out the controller context. The following code is the basis to allowing this.

namespace ProjectNamespace
{
public class FakeControllerContext
{
public ControllerContext ControllerContext { get; private set; }

private readonly Mock<ControllerBase> _mockOfControllerBase;
private readonly Mock<HttpContextBase> _mockOfHttpContextBase;
private readonly Mock<HttpSessionStateBase> _mockOfHttpSessionStateBase;

public FakeControllerContext()
{
_mockOfControllerBase = new Mock<ControllerBase>();
_mockOfHttpContextBase = new Mock<HttpContextBase>();
_mockOfHttpSessionStateBase = new Mock<HttpSessionStateBase>();

ControllerContext = new ControllerContext(_mockOfHttpContextBase.Object, new RouteData(), _mockOfControllerBase.Object);
}
}
}


Once this class has been set-up then you can set the context of your controller as follows: ~

var controller = new YourController() { ControllerContext = new FakeControllerContext().ControllerContext };