Ramblings about Umbraco, .net and JavaScript development. With a sprinkle of other stuff.

Testing views with RazorGenerator

Razor what?

RazorGenerator is a hidden gem that lets you generate and pre-compile what would otherwise be generated and compiled at runtime. Your Razor. Not only does it give you a startup time boost, but it lets you unit test your views. The latter is the focus of this post.

We'll continue to build on the project I described in my post on generating documentation with NUnit. It's a simple use case where an imaginary CMS has a feature to display events from a third party site. Here are the tests I've got so far:

In my previous post, I left the rendering test inconclusive. I like keeping inconclusive tests around as reminders of stuff I've got left to do. Let's have a quick look at the passing code before we dive into the rendering bits.

Basic HTTP integration and conversion tests

The first large piece of what's in there for now is a way to remove the physical dependency on the third party site. I like to stub away IO as far out as I can so I can test as much of my code quickly, yet as integrated as possible. In other words, as many participating classes as possible. Since we're making an example here, I'm just using the HttpClient directly from the controller. The HttpClient is hard to mock or fake, but it has an inner dependency that we can pass as an argument to a constructor: HttpMessageHandler. It has the innermost function that the HttpClient uses for any operation. It also has the rare trait of being protected virtual, so we can stub it out. For this example, I'm just using a fake one that records requests and returns responses for registered URLs. Here it is:

The test is a tedious verification of every property on the event object. There are several ways to get around that. Amongst others, equality members. I've got a way better trick, but that's for an upcoming post.

Now that we're through those tests, let's dive into how we can test the actual HTML output of this whole shenanigan.

Generating some Razor

As mentioned initially, RazorGenerator is a hidden gem in the ASP.NET OSS wilderness. There's a Visual Studio plugin that you need to exploit it fully. It's aptly called RazorGenerator and can be installed from here. There's also a couple of Nuget packages that we want:

RazorGenerator.Mvc
Referenced in your website to make use of a special ViewEngine for pre-compiled views.

Armed with these tools, there's nothing left to do but add a custom tool to our view:

As soon as you've blured the "Custom Tool" field, a .cs file will be generated beneath the view. Upon inspection it yields a class in the ASP namespace making a whole lot of Write* statements. This is what clutters up your ASP.NET Temporary files all the time. And the nice part: it can be instantiated.

I know, the view type name ain't too pretty. But it's necessary for the view engine to find the right one based on the path given. The cool part is the Render statement. It's an extension method from the RazorGenerator.Tests package. It returns the final HTML as a string.

The Console.WriteLine statement yields the following in our test output now:

What we've just done is to test our system almost end to end, cut off at the uttermost borders to external IO. Specifically, the third party site and the end-user's browser.

Granted, we could do Selenium tests here to really test it via the browser, but my rule of thumb is that RazorGenerator is the best choice unless you've got a lot of JavaScript interactivity you need to test in integration. These are subjects for another post.

There is a remaining issue though. We should assert the HTML we got rendered. We could store that HTML we got in a const string expected in our test, but it's gonna foul up the code quite a bit. We could go with so-called "gold files" and implement something to compare a file to the actual output. There's a magical tool for that called ApprovalTests, which I'll cover in my next post.

There's also the option of using HtmlAgilityPack to query the rendered view. The RazorGenerator.Tests package have built-in alternative for Render called RenderAsHtml that returns HtmlDocument instances for you to query. It's quite useful when your only Assert.That is for some specific element in a big page.

For now it doesn't do nested Html.Action or Html.Partial calls. I've got a PR in the works, but I need to polish it for it to get in there. Some day soon. ;) If you really want to, you'll find my fork and build your own, but you'll be on your own.

There's also a tiny performance hit. You'll have to wait a second longer for your tests to execute, since a lot of the ASP.NET MVC framework is spun up to render views. It's still less than the magical 2 second focus cap though, so you should be able to work effectively with it.

I hope this piqued your interest in writing broader tests even up to the UI layers. There's even cooler tricks in store for you if you're already on .net Core, but the rest of us will have to make due until then.