Mocking ASP.NET MVC HtmlHelper extension methods using Moq

by Ben Hart 17. October 2008 06:43

I'm in the process of upgrading our ASP.NET MVC Preview 5 app to Beta. Been quite painless so far, but hit a snag with an unmentioned change to the signature of the ViewContext class.

We've followed many by monkeypatching the HtmlHelper class further, extending it to a variety of uses. Obviously we need to test these extensions, so need a reference to an HtmlHelper instance.  We used to have the following helper method to get an HtmlHelper object:

public HtmlHelper CreateHtmlHelper(ViewDataDictionary viewData)
{
    var sw = new StringWriter();
    var rd = new RouteData();
    var tc = new TestController();
    var td = new TempDataDictionary();
    var tv = new TestView();
    var req = new HttpRequest("", "http://localhost/", "");
    var res = new HttpResponse(sw);
    var hc = new HttpContext(req, res);
    var hcw = new HttpContextWrapper(hc);
    var rc = new RequestContext(hcw, rd);
    var cc = new ControllerContext(rc, tc);
    var vc = new ViewContext(cc, "View", viewData, td);
 
    return new HtmlHelper(vc, tv);
}

I'd been aware of this method (had stumbled across it when certain tests seemed to be taking longer than they should), thought it looked pretty dodgy, ignored it, and added it to the growing list of technical debt. "Well it isn't broken..." I've joked with my teammate responsible about adding it the daily wtf, but we've both agreed we've both seen worse.

The beta of ASP.NET MVC has changed the ViewContext constructor to now require an IView and not a view name string, which fortunately broke the above, allowing me to reclaim some debt. We're using Moq, which allowed the following, much simpler, method.

public static HtmlHelper CreateHtmlHelper(ViewDataDictionary viewData)
{
    var mockViewContext = new Mock<ViewContext>(new Mock<HttpContextBase>().Object, 
                                                    new RouteData(), 
                                                    new Mock<ControllerBase>().Object, 
                                                    new Mock<IView>().Object, 
                                                    viewData,
                                                    new TempDataDictionary());
 
    var mockViewDataContainer = new Mock<IViewDataContainer>();
    mockViewDataContainer.Expect(v => v.ViewData).Returns(viewData);
 
    return new HtmlHelper(mockViewContext.Object, mockViewDataContainer.Object);
}

The HtmlHelper requires a ViewContext, and an IViewDataContainer. Mocking the ViewContext is clearly the most work, but not that dificult. In certain circumstances the HtmlHelper needs to get the ViewDataDictionary off the IViewDataContainer, so the Mock of the container above returns the one we fill with test data, which is passed into the method. This might not be necessary, depending on your situation.

If your helper extensions need more from the HttpContext (such as the Request), obviously you'll need to set those expectations too, ours currently don't. Ben Hall has an similar implementation using RhinoMocks (which would need to be changed to cater for the IView requirement), which sets expectations allowing the Resolve method to be used.

Update: I've since realised that one doesn't really 'Mock' an extension method (or any method for that matter), so don't call me out on the title!

Technorati Tags: ,,

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

ASP.NET MVC | TDD

Comments

Add comment


(Will show your Gravatar icon)  

biuquote
  • Comment
  • Preview
Loading



Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About me...

I'm a passionate .NET developer, with C# my language of choice. I've been at it for a number of years now, and enjoy that I'll never shake the feeling I'm just starting out.

I love software, and I love building it even more. I love knowing that my work facilitates others', and that one line of code at a time, we're increasing our capability.

More...



Page List