Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I correctly mock my controllercontext to test ViewResult.ExecuteResult()?

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()?

like image 988
KallDrexx Avatar asked May 17 '11 02:05

KallDrexx


2 Answers

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.

like image 95
Brian Ball Avatar answered Oct 18 '22 21:10

Brian Ball


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

like image 1
magritte Avatar answered Oct 18 '22 21:10

magritte