Tuesday, December 11, 2007

Mocking HttpContext for MVC route testing

First I have to say thx a bunch to MS for releasing the CTP of the MCV framework. This really is Da Bomb. I think they are doing a great job here.

So I am setting up my first MVC.NET based site, while digging through all the relevant posts on how to use the framework. So while setting up my routes, doing the proper TDD, I stumble upon the MockHttpContext for testing routes in ScottGu's part 2. This bugger is not explained anywhere nor part of the framework. So you have to whip up your own. Googling it - lazy as I am turned up nothing. DIY time. But being the friendly coder, I will share what I made.

So without further ado - here is how you could create a MockHttpContext for testing your routes using Rhino.Mocks. Note, I have used the same way of setting up the routes as Scott is showing in his post.

[TestFixture]
public class RoutesTest
{
protected RouteCollection routes;
protected MockRepository mockery;

[SetUp]
public void SetUp()
{
routes = new RouteCollection();
Global application = new Global();
application.RegisterRoutes(routes);
mockery = new MockRepository();
}

protected IHttpContext MockContextFactory(string url)
{
IHttpContext httpContext = mockery.Stub<IHttpContext>();
IHttpRequest httpRequest = mockery.Stub<IHttpRequest>();
SetupResult.For(httpRequest.AppRelativeCurrentExecutionFilePath).Return("~" + url);
SetupResult.For(httpContext.Request).Return(httpRequest);
return httpContext;
}

[Test]
public void TestRouteRegistration()
{
Assert.IsNotEmpty(routes, "We have filled the routes list");
}

[Test]
public void SearchRouteExists()
{
IHttpContext httpContext;
using (mockery.Record())
{
httpContext = MockContextFactory("/Search/Beverages/2");
}
using (mockery.Playback())
{
RouteData routeData = routes.GetRouteData(httpContext);
Assert.IsNotNull(routeData, "There is a route to the URL");
Assert.AreEqual("Search", routeData.Values["controller"], "We are using the search controller");
Assert.AreEqual("Results", routeData.Values["action"], "With the details action");
Assert.AreEqual("Beverages", routeData.Values["query"], "And the correct query");
Assert.AreEqual("2", routeData.Values["page"], "And the correct page index");
}
}
}
kick it on DotNetKicks.com

7 comments:

chief7 said...

Thanks for the post! I am new to unit testing and didn't know how to create a mock HttpContext.

I did wonder though is there anyway to abstract out the Record?Playback so it is cleaner in the actual test? Scott's example was very clean.

Haacked said...

Nice, I did something similar as an extension method:
http://haacked.com/archive/2007/11/05/rhino-mocks-extension-methods-mvc-crazy-delicious.aspx

Fehaar said...
This comment has been removed by the author.
Fehaar said...

@haacked: I had actually read your post I see now - but it had slipped my mind when time came to produce code. Will take another look at it. Haven't gotten used to writing extension methods yet!

Fehaar said...

@chris7: Was preparing a reply to you, but it turned into a post. Code does not look good in comments!
http://fehaar.blogspot.com/2007/12/more-mocking-of-httpcontext-for-mvc.html

noodle said...

I am having a small issue with the MockHttpContext. I don't know where you are referencing your IHttpContext interface but I got mine from Phill Haack. http://haacked.com/archive/2007/11/05/rhino-mocks-extension-methods-mvc-crazy-delicious.aspx

The problem is, for the line where you say routes.GetRouteData(httpContext), I have to cast httpContext to System.Web.HttpContextBase or else my program won't compile. But the object does not cast and leaves a null (my test fails at that point, anyway). Any ideas?

Fehaar said...

Please note that the above code is for Preview 1. Things have changed a lot since that.

If you have NUnit and Rhino mocks installed with preview 3 and start a new project using that, you will get a class in your test project with a helper for creating a fake Http context. This is the same as what I do here.