Monday, March 7, 2011

How can I unit test an MVC UserViewControl?

I've been trying for a while now to write a unit test for a UserViewControl in ASP.NET MVC. I'd like to get to code that looks something like this:

[TestMethod]
public void HaveControlToDisplayThings()
{
    var listControl = new ControlUnderTest();
    var viewData = new ViewDataDictionary<IList<string>>(this.repo.GetMeSomeData());

    // Set up a ViewContext using Moq.
    listControl.SetFakeViewContext(viewData);
    listControl.ViewData = viewData;
    listControl.RenderView(listControl.ViewContext);

    // Never got this far, no idea if this will work :)
    string s = listControl.ViewContext.HttpContext.Response.Output.ToString();
    Assert.AreNotEqual(0, s.Length);
    foreach (var item in this.repo.GetMeSomeData())
    {
        Assert.IsTrue(s.IndexOf(item) != -1);
    }
}

Unfortunately, no matter what I try I get errors from deep inside RenderView. This is caused (as far as I can tell) by the static HttpContext.Current object being useless - I get NullReferenceExceptions from System.Web.UI.Page.SetIntrinsics.

I tried using Phil Haack's HttpSimulator which gave me a HttpContext object but I found I also needed to specify a fake HttpBrowserCapabilities object to get slightly further:

Subtext.TestLibrary.HttpSimulator simulator = new HttpSimulator();
simulator.SimulateRequest();
var browserMock = new Mock<HttpBrowserCapabilities>();
browserMock.Expect(b => b.PreferredRenderingMime).Returns("text/html");
browserMock.Expect(b => b.PreferredResponseEncoding).Returns("UTF-8");
browserMock.Expect(b => b.PreferredRequestEncoding).Returns("UTF-8");
HttpContext.Current.Request.Browser = browserMock.Object;

Now I get exceptions on property accesses on that object. I mocked as many as I could, but seemed to be getting nowhere fast.

Has anyone managed to make this work?

From stackoverflow
  • One option would be to run unit tests inside the browser. I've had success with Selenium for such cases.

  • We gave up on unit testing views and are now using WatiN browser tests as part of our build.

    We also use Resharper Solution Wide Analysis to check if there are compiler errors. Not perfect, but it gets very similar results. Downside - WatiN tests are slow.

  • Unfortunately, the ASP.NET viewengine uses the VirtualPathProvider in the ASP.NET hosting environment. To make matters worse, I traced some of the other code using Reflector and found that there is other dependencies to some hardcode references to VirtualPath utilities. I hope they fix this in the release so we can truly test our Views and how they are rendered.

    Simon Steele : Accepting this as the answer because it seems it's essentially: No, there is no way to **unit test** the result of rendering views. Using selenium, Watin or equivalent is a good alternative, but it's not what I really want to do - browserless testing of control/view output.
  • These are the values that need to be set in the HttpBrowserCapabilities object for a asp.net webforms site to run, I would try making sure these are set and see if that fixes your problem, I'm not sure if it would but hey it worth a shot right?

    • Browser (aka name)
    • useragent (passed in the request)
    • tables (true/false)
    • version (version of browser eg 1.0)
    • w3cdomversion (eg 1.0)
    • cookies (true/false)
    • ecmascriptversion (eg 1.0)

    Hope this helps.

    Simon Steele : Thanks, I'll give this a try next time I'm working on that code.
  • I would recommend selenium as well for the UI testing. There is a quite a bit in a standard MVC application that can be unit tested, but the UI level components always seemed a better fit for in-browser testing like Selenium. You can integrate Selenium testing with your unit testing using cruisecontrol.net.

    Here is a guide for integrating Selenium with your CC.Net.

  • Use TypeMock to mock away the dependencies. I have written one blog post about how to mock out the Request and Response dependencies in Controller layer. Maybe it's helpful.

0 comments:

Post a Comment