5
votes

I'm attempting to unit test a custom view engine I've written.

The intended functionality of the view engine is that it alters where the base RazorViewEngine looks when performing FindView

Here is my unit test

public void ViewEngineReturnsDependencyView()
{
    //Mock http request
    var mockRequest = new Mock<HttpRequestBase>();
    //Mock server variable
    NameValueCollection variables = new NameValueCollection();
    variables.Add("APPL_PHYSICAL_PATH", TEST_APPLICATION_PATH);
    mockRequest.Setup(r => r.ServerVariables).Returns(variables);

    //Mock http context
    var mockHttpContext = new Mock<HttpContextBase>();

    //Mock route
    mockHttpContext.Setup(c => c.Request).Returns(mockRequest.Object);
    var routeData = new RouteData();
    routeData.Values.Add("controller", "testController");
    routeData.Values.Add("action", "testAction");

    //Mock controller context
    var controllerContext = new testController().ControllerContext;
    controllerContext.HttpContext = mockHttpContext.Object;
    controllerContext.RouteData = routeData;
    var mockControllerContext = new ControllerContext(mockHttpContext.Object,
                        routeData, 
                        new Mock<ControllerBase>().Object);

    //Run find view
    viewEngine.FindView(mockControllerContext, "TestView", null, false);
}

Annoyingly viewEngine.FindView(...); throws an exception:

Test method ... threw exception: System.NullReferenceException: Object reference not set to an instance of an object. Result StackTrace:

at System.Web.WebPages.DisplayModeProvider.GetDisplayMode(HttpContextBase context)

at System.Web.Mvc.VirtualPathProviderViewEngine.GetPathFromGeneralName(ControllerContext controllerContext, List`1 locations, String name, String controllerName, String areaName, String cacheKey, String[]& searchedLocations)

at System.Web.Mvc.VirtualPathProviderViewEngine.GetPath(ControllerContext controllerContext, String[] locations, String[] areaLocations, String locationsPropertyName, String name, String controllerName, String cacheKeyPrefix, Boolean useCache, String[]& searchedLocations)

at System.Web.Mvc.VirtualPathProviderViewEngine.FindView(ControllerContext controllerContext, String viewName, String masterName, Boolean useCache)

at ...Mvc.CustomRazorViewEngine.FindView(ControllerContext controllerContext, String viewName, String masterName, Boolean useCache) in ...\Mvc\CustomRazorViewEngine.cs:line 85

at ...Tests.MVC.ViewEngine.ViewEngineReturnsDependencyView() in ...Tests\MVC\ViewEngine.cs:line 78

My question is, how do create the proper mocks to unit test RazorViewEngine.FindView()?

2

2 Answers

8
votes

The System.Web.WebPages.DisplayModeProvider.GetDisplayMode() method is using the HttpContext.Items property, you need to Mock that property as well.

Try:

mockHttpContext.Setup(c => c.Items).Returns(new Dictionary<object, object>());
2
votes

I know, that I'm possibly late, but I had same problems. After some research I found the way how razor engine was tested by Microsoft team. You can find it here: http://aspnetwebstack.codeplex.com/SourceControl/latest#test/System.Web.Mvc.Test/Test/RazorViewEngineTest.cs The basic idea is to create testable view engine stub, that explicitly exposes protected methods of view engine. ir is called TestableRazorViewEngine and you can find it at the end of the file. I changed it a bit corresponding to my needs. I added 3-rd argument to every stub view engine's method of type ControllerContext to pass controller metadata to my view engine. This is the way how I did it:

[Fact]
public void CreatePartialView_ViewNameWithoutReplacementToken_ReturnsOriginalPath()
{
    var engine = new TestableViewEngine();
    var view = (RazorView)engine.CreatePartialView("partial path", new ControllerContext());
    Assert.Equals("partial path", view.ViewPath);
}

[Fact]
public void CreatePartialView_ViewNameWithReplacementToken_ReturnsViewWithTokenReplacedByControllerNamespace()
{
    var engine = new TestableViewEngine();
    var controller = new DummyController();
    var controllerContext = new ControllerContext { Controller = controller };
    var view = (RazorView)engine.CreatePartialView("partial path %1", controllerContext);
    Assert.Equals("partial path Stub/Tests/Controllers", view.ViewPath);
}