Unit testing razor views

Reading time ~2 minutes

If you are developing a web site using ASP.NET MVC it's possible that you are using razor in your views. And it's possible that you have some presentation logic in that views. This code is hard to test. We love testing our code, so probably you'll end up coding some Selenium or CodedUI tests. As you may know, UI tests are more fragile, harder and slower to execute than unit tests and therefore we execute them less. UI tests are good in some cases but if we could replace them with a more reliable suite of tests, that would be great.

And here is where RazorGenerator extension and NuGet packages (created by David Ebbo) come to rescue. What does this extension? It pre-compiles locally your views (the same code that is generated by IIS when you load a page is generated locally) so mainly you win three things:

  • The initial loading of your website will be faster because the view is already compiled.
  • You can test your views.
  • You don't need to deploy your cshtml files.

The first step is to install the extension, so go to the Extensions and Updates section of Visual Studio and search for RazorGenerator.

[caption id="attachment_219" align="alignnone" width="1024"]RazorGenerator extension RazorGenerator extension[/caption]

With the extension installed you are already able to pre-compile your views, but if you want to use them in your site, you should install RazorGenerator.Mvc NuGet package in your web project.

To generate the code version of your view you must set RazorGenerator as the custom tool of the views.

[caption id="attachment_220" align="alignnone" width="1023"]RazorGenerator custom tool RazorGenerator custom tool[/caption]

And that's all. The extension you've just installed will generate the compiled version of the view.

[caption id="attachment_221" align="alignnone" width="379"]Pre-compiled view Pre-compiled view[/caption]

Now you have a code version of your view, so you can test it. Go to your test project and install the RazorGenerator.Testing package. A typical test could be this one:

[TestMethod]
public void TestUsingRoutes()
{
    // Instantiate the view directly
    var view = new UsingRoutes();

    // Set up the data that needs to be access by the view
    MvcApplication.RegisterRoutes(RouteTable.Routes);

    // Render it in an HtmlDocument
    var output = view.RenderAsHtml();

    // Verify that it looks correct
    var element = output.GetElementbyId("link-using-routes");
    Assert.AreEqual("/Home/UsingRoutes", element.Attributes["href"].Value);
}

Where UsingRoutes is the name of the view we want to test. As you can see, the package provides us with an extension method for the view called RenderAsHtml (and another one called RenderAsString). This method returns an HtmlNode object of the HtmlAgilityPack library that we can query easily to verify if the view is correctly rendered.

It's possible that you will need to setup an HttpContext object (using mocks) to simulate some behavior. The library provides us a simple builder called HttpContextBuilder that you can use for this purpose. Let's see an example:

[TestMethod]
public void TestMockHttpContext()
{
    // Instantiate the view directly
    var view = new MockHttpContext();

    // Set up the data that needs to be access by the view
    var mockHttpRequest = new Mock<HttpRequestBase>(MockBehavior.Loose);
    mockHttpRequest.Setup(m => m.IsAuthenticated).Returns(true);

    // Render it in an HtmlDocument
    var output = view.RenderAsHtml(new HttpContextBuilder().With(mockHttpRequest.Object).Build());

    // Verify that it looks correct
    var element = output.GetElementbyId("user-authenticated");
    Assert.IsNotNull(element);   
}

As you can see we are setting up a mock of the HttpRequest object to specify that the user is authenticated. We can pass an HttpContext created with the builder as the first parameter of the RenderAsHtml call.

And that's all. Testing is not the main purpose of RazorGenerator project, but it's a nice side effect.

Analysing hotspots using CrystalGazer and NDepend

When you start working on a new project, there are a couple of things that you should try to discover as fast as you can: the shape of th...… Continue reading

ANTLR and JavaScript

Published on September 06, 2017

Calling a Step Function

Published on July 09, 2017