Rhino Mocks + Extension Methods + MVC == Crazy Delicious

asp.net, code, asp.net mvc, tdd 0 comments suggest edit

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.

Tags: ASP.NET MVC , TDD, Rhino Mocks

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

15 responses

  1. Avatar for Haacked
    Haacked November 5th, 2007

    By the way: X + Y == Crazy Delicious is officially overplayed and I apologize for using it as the title of this blog post. :)

  2. Avatar for Sergio Pereira
    Sergio Pereira November 5th, 2007

    I think you meant:
    SetupResult.For(request.HttpMethod).Return(httpMethod);

  3. Avatar for Haacked
    Haacked November 5th, 2007

    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.

  4. Avatar for Kevin Dente
    Kevin Dente November 5th, 2007

    OK, now you're just teasing us poor bastards chomping at the bit to get our hands on ASP.NET MVC. :P

  5. Avatar for Tim Rayburn
    Tim Rayburn November 5th, 2007

    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>();
    }

  6. Avatar for Karthik Hariharan
    Karthik Hariharan November 5th, 2007

    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.

  7. Avatar for Haacked
    Haacked November 5th, 2007

    @Tim - But extension methods apply to instances. How do you get an instance of IHttpContext for which to mock?

  8. Avatar for Tales from the SharpSide
    Tales from the SharpSide November 5th, 2007

    Links from the Sharpside [Guy Fawkes Edition]

  9. Avatar for The RobLog
    The RobLog November 7th, 2007

    DevConnections - MVC Framework for ASP.NET

  10. Avatar for Aaron Jensen
    Aaron Jensen November 7th, 2007

    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/20...

  11. Avatar for Haacked
    Haacked November 13th, 2007

    I tried that, Aaron, but it didn't work because I still want to setup expectations.

  12. Avatar for Community Blogs
    Community Blogs December 9th, 2007

    Just a brief note on writing unit tests for controller actions. When your action has a call to RedirectToAction

  13. Avatar for you've been HAACKED
    you've been HAACKED December 16th, 2007

    Testing Routes In ASP.NET MVC

  14. Avatar for ASPInsiders
    ASPInsiders December 18th, 2007

    Kzu and friends have a new pet project called Moq , which may be the coolest derivative open source project

  15. Avatar for Tom
    Tom March 28th, 2008

    Thanks Phil,
    Enlightening as usual.