7

I am attempting to create integration tests to make sure my views do not have any runtime errors in them. Thus I need to create a test that checks if ViewResult.ExecuteResult() works correctly but it seems I have hit a snag.

I found this site which gave me a starting point, and I have the following code:

    [TestMethod]
    public void RegisterResultExecutes()
    {
        //arrange 
        RequestContext requestContext = new RequestContext(new MockHttpContext(), new RouteData());
        AccountController controller = new AccountController
        {
            FormsService = new MockFormsAuthenticationService(),
            MembershipService = new MockMembershipService(),
            Url = new UrlHelper(requestContext)
        };

        var result = controller.Register();
        var sb = new StringBuilder();
        Mock<HttpResponseBase> response = new Mock<HttpResponseBase>();
        response.Setup(x => x.Write(It.IsAny<string>())).Callback<string>(y =>
        {
            sb.Append(y);
        });
        Mock<ControllerContext> controllerContext = new Mock<ControllerContext>();
        controllerContext.Setup(x => x.HttpContext.Response).Returns(response.Object);

        //act 
        result.ExecuteResult(controllerContext.Object);
    }

The problem is that when result.ExecuteResult() is called I get the following exception

System.NullReferenceException: Object reference not set to an instance of an object.

System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
MyApp.Tests.Controllers.AccountControllerTest.RegisterResultExecutes() in C:\Users\KallDrexx\Documents\Projects\MyApp\MyApp.Tests\Controllers\AccountControllerTests.cs: line 297

Unfortunately, that stack trace isn't very useful as I'm not sure what it's trying to access that is null. Does anyone have any suggestions on how I can create a test for ExecuteResult()?

KallDrexx
  • 27,229
  • 33
  • 143
  • 254

2 Answers2

4

Based on the stack trace, it is something in the ViewResultBase.ExecuteResult method that throws the exception. Using reflector, here is the definition of that method:

public override void ExecuteResult(ControllerContext context)
{
    if (context == null)
    {
        throw new ArgumentNullException("context");
    }
    if (string.IsNullOrEmpty(this.ViewName))
    {
        this.ViewName = context.RouteData.GetRequiredString("action");
    }
    ViewEngineResult result = null;
    if (this.View == null)
    {
        result = this.FindView(context);
        this.View = result.View;
    }
    TextWriter output = context.HttpContext.Response.Output;
    ViewContext viewContext = new ViewContext(context, this.View, this.ViewData, this.TempData, output);
    this.View.Render(viewContext, output);
    if (result != null)
    {
        result.ViewEngine.ReleaseView(context, this.View);
    }
}

Based on that code, an object reference exception could be thrown when the code tries to access the RouteData property from the context (if the name of the view wasn't explicitly given to the return type).

The exception could be thrown by accessing the HttpContext property. I haven't used Moq well enough to know if it can handle the fact that you haven't told it how to mock the HttpContext property, but you have told it how to mock the Response property from the HttpContext property's type, so that's another area that is suspect to me.

All other uses of context in the method are passing it into other methods, which if those were the problem, then the stack trace would have revealed that.

The easiest way to see which of the two I mentioned are the problem, I would write a quick test to pull those properties from your mocks and see which one causes the exception.

Brian Ball
  • 12,268
  • 3
  • 40
  • 51
  • Aha, that reflection gives a lot of information. It was `RouteData` that was causing the issue, but now it seems like I need to find a way for `context.RouteData.GetRequiredString("action");` to return something useful, as that's where I am stuck now – KallDrexx May 17 '11 at 03:19
  • Well after setting up `RouteData` correctly, it seems like I'm at the limit of what I can do, and this seems to be impossible. The call to `FindView()` gets a `NullReferenceException` 15 (literally) layers deep in the stack trace in `System.Web.Compilation.BuildManager.GetCacheKeyFromVirtualPath()`. Oh well :( – KallDrexx May 17 '11 at 03:31
  • I was afraid you'd run into a show stopper like that. Is there something specific you are trying to test in your views? If you want to make sure your lambda expressions are fine (if you are using any) you can [compile your views](http://stackoverflow.com/questions/383192/compile-views-in-asp-net-mvc). If you want to test javascript, then I would google around for javascript unit tests as I know there are frameworks out there for that, but I haven't used any. Or are you trying to unit test something else all together? – Brian Ball May 17 '11 at 12:13
  • 1
    I was trying to create unit tests so I would have test-time checking of any view compile errors (without compiling views every time as it is slow, or risk forgetting to switch configurations to compile views prior to pushing to production), and to check for runtime errors (Controller sending a viewmodel that's not expected by the view, invalid casts, incorrect calls to helpers or actions, etc...). It seems like views are areas where code will reside but cannot be tested, and has the potential for hidden exceptions/errors, even on some simple views. – KallDrexx May 17 '11 at 12:31
1

I hit the same problem as this just now and resolved it by setting the HttpContext.Current.

Try adding the following to your unit test code: e.g.

HttpContext.Current = new HttpContext(
    new HttpRequest("", "http://mock", ""),
    new HttpResponse(new StringWriter()));

One thing that I found useful for debugging this sort of problem rather than using reflector or ILSpy is to turn on debug symbols for the .Net framework code. That way you can attach to your NUnit process and see exactly what line of code is throwing the exception and therefore what you need to Mock in the test.

Shawn Burke has written an excellent blog article detailing how to set this up here: http://blogs.msdn.com/b/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx

magritte
  • 7,396
  • 10
  • 59
  • 79