Rhino Mocks + Extension Methods + MVC == Crazy Delicious

UPDATE: This content is a bit outdated as these interfaces have changed in ASP.NET MVC since the writing of this post.

One task that I relish as a PM on the ASP.NET MVC project is to build code samples and sample applications to put the platform through its paces and try to suss out any problems with the design or usability of the API.

Since testability is a key goal of this framework, I’ve been trying to apply a Test Driven Development (TDD) approach as I build out the sample applications. This has led to some fun discoveries in terms of using new language features of C# to improve my tests.

For example, the MVC framework will include interfaces for the ASP.NET intrinsics. So to mock up the HTTP context using Rhino Mocks, you might do the following.

MockRepository mocks = new MockRepository();
      
IHttpContext context = mocks.DynamicMock<IHttpContext>();
IHttpRequest request = mocks.DynamicMock<IHttpRequest>();
IHttpResponse response = mocks.DynamicMock<IHttpResponse>();
IHttpServerUtility server = mocks.DynamicMock<IHttpServerUtility>();
IHttpSessionState session = mocks.DynamicMock<IHttpSessionState>();

SetupResult.For(context.Request).Return(request);
SetupResult.For(context.Response).Return(response);
SetupResult.For(context.Server).Return(server);
SetupResult.For(context.Session).Return(session);

mocks.ReplayAll();
//Ready to use the mock now

Kind of a mouthful, no?

Then it occurred to me. I should use C# 3.0 Extension Methods to create a mini DSL (to abuse the term) for building HTTP mock objects. First, I wrote a simple proof of concept class with extension methods.

public static class MvcMockHelpers
{
  public static IHttpContext 
    DynamicIHttpContext(this MockRepository mocks)
  {
    IHttpContext context = mocks.DynamicMock<IHttpContext>();
    IHttpRequest request = mocks.DynamicMock<IHttpRequest>();
    IHttpResponse response = mocks.DynamicMock<IHttpResponse>();
    IHttpSessionState session = mocks.DynamicMock<IHttpSessionState>();
    IHttpServerUtility server = mocks.DynamicMock<IHttpServerUtility>();

    SetupResult.For(context.Request).Return(request);
    SetupResult.For(context.Response).Return(response);
    SetupResult.For(context.Session).Return(session);
    SetupResult.For(context.Server).Return(server);

    mocks.Replay(context);
    return context;
  }

  public static void SetFakeHttpMethod(
    this IHttpRequest request, string httpMethod)
  { 
    SetupResult.For(request.HttpMethod).Return(httpMethod);
  }
}

And then I rewrote the setup part for the test (the rest of the test is omitted for brevity).

MockRepository mocks = new MockRepository();
IHttpContext context = mocks.DynamicIHttpContext();
context.Request.SetFakeHttpMethod("GET");
mocks.ReplayAll();

That’s much cleaner, isn’t it?

Please note that I call the Replay method on the IHttpContext mock. That means you won’t be able to setup any more expectations on the context. But in most cases, you won’t need to.

This is just a proof-of-concept, but I could potentially add a bunch of SetFakeXYZ extension methods on the various intrinsics to make setting up expectations and results much easier. I chose the pattern of using the SetFake prefix to help differentiate these test helper methods.

Note that this technique isn’t specific to ASP.NET MVC. As you start to build apps with #C 3.0, you can build extensions for commonly used mocks to make it easier to write unit tests with mocked objects. That takes a lot of the drudgery out of setting up a mocked object.

Oh, and if you’re lamenting the fact that you’re writing ASP.NET 2.0 apps that don’t have interfaces for the HTTP intrinsics, you should read my post on IHttpContext and Duck Typing in which I provide such interfaces.

Happy testing to you!

I have a follow-up post on testing routes. The project includes a slightly more full featured version of the MvcMockHelpers class.

What others have said

Requesting Gravatar... Haacked Nov 05, 2007 12:25 PM
# re: Rhino Mocks + Extension Methods + MVC == Crazy Delicious
By the way: X + Y == Crazy Delicious is officially overplayed and I apologize for using it as the title of this blog post. :)
Requesting Gravatar... Sergio Pereira Nov 05, 2007 1:29 PM
# re: Rhino Mocks + Extension Methods + MVC == Crazy Delicious
I think you meant:
SetupResult.For(request.HttpMethod).Return(httpMethod);
Requesting Gravatar... Haacked Nov 05, 2007 1:35 PM
# re: Rhino Mocks + Extension Methods + MVC == Crazy Delicious
Yeah, but there's a problem with that I just realized. I'm updating the post. You can't access context.Request until you call mocks.ReplayAll(). But then you can't set any more expectations.
Requesting Gravatar... Kevin Dente Nov 05, 2007 1:44 PM
# re: Rhino Mocks + Extension Methods + MVC == Crazy Delicious
OK, now you're just teasing us poor bastards chomping at the bit to get our hands on ASP.NET MVC. :P
Requesting Gravatar... Tim Rayburn Nov 05, 2007 2:21 PM
# re: Rhino Mocks + Extension Methods + MVC == Crazy Delicious
Interesting idea, but I would have used the same functionality (Extension Methods) to "skin this cat" the other way around. That is to say, I would have extended IHttpContext to know how to Mock itself instead. Your way is very traditional OO, the MockRepository's singe role is Mocks, but I think the code is more readable the other way, personally. You can create a catch-all routine like this one below, and then create specific routine for complex types like IHttpContext above:

public static T Mock<T>(this T obj, MockRepository mocks)
{
return mocks.DynamicMock<T>();
}
Requesting Gravatar... Karthik Hariharan Nov 05, 2007 2:34 PM
# re: Rhino Mocks + Extension Methods + MVC == Crazy Delicious
Interesting solution. I'm eager to play with the CTP of ASP.NET MVC. Now I don't know whether to use it or Silverlight on a pet project at work :P

There's some logic to what Tim is saying in his comment above if you think about it. But an interface that can fully mock itself just seems weird and my OO-compliant mind just can't get past it. But it might be worth exploring.
Requesting Gravatar... Haacked Nov 05, 2007 3:34 PM
# re: Rhino Mocks + Extension Methods + MVC == Crazy Delicious
@Tim - But extension methods apply to instances. How do you get an instance of IHttpContext for which to mock?
Requesting Gravatar... Tales from the SharpSide Nov 05, 2007 6:43 PM
# Links from the Sharpside [Guy Fawkes Edition]
Links from the Sharpside [Guy Fawkes Edition]
Requesting Gravatar... The RobLog Nov 07, 2007 12:49 PM
# DevConnections - MVC Framework for ASP.NET
DevConnections - MVC Framework for ASP.NET
Requesting Gravatar... Aaron Jensen Nov 08, 2007 6:36 AM
# re: Rhino Mocks + Extension Methods + MVC == Crazy Delicious
You can get rid of 6 lines of code by using a stub:
IHttpContext context = mocks.GenerateStub<IHttpContext>();
context.Request = mocks.DynamicMock<IHttpRequest>();
context.Response = mocks.DynamicMock<IHttpResponse>();
context.Session = mocks.DynamicMock<IHttpSessionState>();
context.Server = mocks.DynamicMock<IHttpServerUtility>();

This has the added benefit of allowing people to replace any of the children with fakes or stubs.

You could also accomplish the same thing in a nicer way if you write a concrete implementation of IHttpContext with constructor injection for its properties and use the auto mocking container to inject the context into your controller: http://blog.eleutian.com/2007/08/05/UsingTheAutoMockingContainer.aspx
Requesting Gravatar... Haacked Nov 13, 2007 9:48 PM
# re: Rhino Mocks + Extension Methods + MVC == Crazy Delicious
I tried that, Aaron, but it didn't work because I still want to setup expectations.
Requesting Gravatar... Community Blogs Dec 09, 2007 9:26 PM
# Writing Unit Tests For Controller Actions
Just a brief note on writing unit tests for controller actions. When your action has a call to RedirectToAction
Requesting Gravatar... you've been HAACKED Dec 17, 2007 1:06 AM
# Testing Routes In ASP.NET MVC
Testing Routes In ASP.NET MVC
Requesting Gravatar... Scott Hanselman's Computer Zen Dec 19, 2007 12:59 AM
# Moq: Linq, Lambdas and Predicates applied to Mock Objects
Requesting Gravatar... ASPInsiders Dec 19, 2007 1:00 AM
# Moq: Linq, Lambdas and Predicates applied to Mock Objects
Kzu and friends have a new pet project called Moq , which may be the coolest derivative open source project
Requesting Gravatar... Scott Hanselman's Computer Zen Dec 19, 2007 10:34 AM
# Moq: Linq, Lambdas and Predicates applied to Mock Objects
Requesting Gravatar... Tom Mar 29, 2008 6:44 AM
# re: Rhino Mocks + Extension Methods + MVC == Crazy Delicious
Thanks Phil,

Enlightening as usual.

What do you have to say?

(will show your gravatar)
Please add 6 and 5 and type the answer here: