Using QUnit with Razor Layouts

asp.net, asp.net mvc, tdd, code, razor 0 comments suggest edit

Given how central JavaScript is to many modern web applications,  it is important to use unit tests to drive the design and quality of that JavaScript. But I’ve noticed that there are a lot of developers that don’t know where to start.

There are many test frameworks out there, but the one I love is QUnit, the jQuery unit test framework.

qunit-tests-running

Most of my experience with QUnit is writing tests for a client script library such as a jQuery plugin. Here’s an example of one QUnit test file I wrote a while ago (so you know it’s nasty).

You’ll notice that the entire set of tests is in a single static HTML file.

I saw a recent blog post by Jonathan Creamer that uses ASP.NET MVC 3 layouts for QUnit tests. It’s a neat approach that consolidates all the QUnit boilerplate into a single layout page. This allows you to have multiple test files and duplicate that boilerplate.

But there was one thing that nagged me about it. For each new set of tests, you need to add an action method and a corresponding view. ASP.NET MVC does not allow rendering a view without a controller action.

Controller-Less Views

The idea of controller-less views has been one tossed around by folks, but there are all sorts of design issues that come up when you consider it. For example, how do you request such a view directly? If you allow that, what if the view is intended to be rendered by a controller action. Now you have two ways to access that view, one of which is probably incorrect. And so on.

However, there is another lesser known framework (at least, lesser known to ASP.NET MVC developers) from the ASP.NET team that pretty much provides this ability!

ASP.NET Web Pages with Razor Syntax

It’s a product called ASP.NET Web Pages that is designed to appeal to developers who prefer an approach to web development that’s more like PHP or classic ASP.

Aside: I’d like to go on record and say I hated that name from the beginning because it causes so much confusion. Isn’t everything I do in ASP.NET a web page?

A Web Page in ASP.NET Web Pages (see, confusing!) uses Razor syntax inline to render out the response to a request. ASP.NET Web Pages also support layouts. This means we can create an approach very similar to Jonathan’s, but we only need to add one file for each new set of tests. Even better, this approach works for both ASP.NET MVC 3 and ASP.NET Web Pages.

The Code

The code to do this is straightforward. I just created a folder named test which will contain all my unit tests. I added an _PageStart.cshtml file to this directory that sets the layout for each page. Note that this is equivalent to the _ViewStart.cshtml page in ASP.NET MVCs.

@{
    Layout = "_Layout.cshtml";
}

The next step is to write the layout file, _Layout.cshtml. This contains the QUnit boilerplate along with a place holder (the RenderBody call) for the actual tests.

<!DOCTYPE html>

<html>
    <head>
        <title>@Page.Title</title>
        <link rel="stylesheet" href="/content/qunit.css " />
        <script src="/Scripts/jquery-1.7.1.min.js"></script>
        <script src="/scripts/qunit.js"></script>

        @RenderSection("Javascript", false)
        @* Tests are written in the body. *@
        @RenderBody()
    </head>
    <body>
        <h1 id="qunit-header">
          @(Page.Title ?? "QUnit tests")
        </h1>
        <h2 id="qunit-banner">
        </h2>
        <h2 id="qunit-userAgent"></h2>
        <ol id="qunit-tests">
        </ol>
        <p>
            <a href="/tests">Back to tests</a>
        </p>
    </body>
</html>

And now, one or more files that contain the actual test. Here’s an example called footest.cshtml.


@{
  Page.Title = "FooTests";
}
@if (false) {
  // OPTIONAL! QUnit script (here for intellisense)
  <script src="/scripts/qunit.js"> </script>
}

<script src="/scripts/calculator.js"></script>


<script>
  $(function () {
    // calculator_tests.js
    module("A group of tests get's a module");
    test("First set of tests", function () {
      var calc = new Calculator();
      ok(calc, "My caluculator is a O.K.");
      equals(calc.add(2, 2), 4, "shit broken");
    });
  });
</script>

You’ll note that I have this funky if (false) block in the code. That’s to workaround a current limitation in Razor so that JavaScript Intellisense for QUnit works in this file. If you don’t care for Intellisense, you don’t need it. I hope that in the future, Razor will pick up the script in the layout and you won’t need this either way.

With this in place, to add a new test with the proper QUnit boilerplate is very easy. Just add a .cshtml file, set the title for the tests, and then add the script you’re testing and the test script into the same file.

The last step is to create an index into all the tests. I wrote the following index.cshtml file that creates a list of links for each set of tests. It simply iterates through every test file and generates a link. One nifty little perk of using ASP.NET Web Pages is you can leave off the extension when you request the file.

@using System.IO;
@{
  Layout = null;

  var files = from path in
  Directory.GetFiles(Server.MapPath("./"), "*.cshtml")
  let fileName = Path.GetFileNameWithoutExtension(path)
  where !fileName.StartsWith("_")
  && !fileName.Equals("index", StringComparison.OrdinalIgnoreCase)
  select fileName;
}

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <div>
        <h1>QUnit tests</h1>
        <ul>
        @foreach (var file in files) {
            <li><a href="@file">@file</a></li>
        }
        </ul>
    </div>
</body>
</html>

The output of this page isn’t pretty, but it works. When I navigate to /test I see a list of my test files:

qunit-tests

Here’s the contents of my test folder when I’m done with all this.

solution

Summary

I personally haven’t used this approach yet, but I think it could be a nice approach if you tend to have more than one QUnit test file in your projects and you tend to customize the boilerplate for those tests.

I tend to just use a static HTML file, but so far, most of my QUnit tests are for a single JavaScript library. But this approach might come in handy when I get around to testing the JavaScript in the NuGet gallery.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

23 responses

  1. Avatar for Daniel15
    Daniel15 December 10th, 2011

    Surely using ASP.NET WebForms with a code-behind is cleaner than this "ASP.NET Web Pages" thing? It seems like WebForms except using Razor, and with the logic mashed into the page.

  2. Avatar for Saeed Neamati
    Saeed Neamati December 10th, 2011

    I see the benefits, but I haven't seen real projects that use unit testing for their JavaScript. I think mentioning some real projects could become motivational, because right now, I only use it in a stand-alone project, only to test it.

  3. Avatar for Daniel15
    Daniel15 December 10th, 2011

    @Saeed: More and more web apps are using JavaScript extensively, and I think unit testing would be important for any large web apps (think things like Gmail and Facebook).

  4. Avatar for Mike
    Mike December 10th, 2011

    Talk about confusing:
    "I hope that in the future, Razor will pick up the script in the layout and you won’t need this either way."
    Hoping that a syntax will 'pick up' a script to provide IntelliSense in an IDE...

  5. Avatar for Giovanni Bassi
    Giovanni Bassi December 10th, 2011

    Maybe you'd like to take a look at a friend's blog post where he runs QUnit tests on Nodejs. For libs that don't need the browser, it is a great thing:
    http://bit.ly/vG7mNm
    Cheers!

  6. Avatar for haacked
    haacked December 10th, 2011

    @Mike "Razor" is also the parser. It might need to change to expose such details for the feature I described to work. I haven't dug into it though.

  7. Avatar for Tugberk
    Tugberk December 10th, 2011

    You continue rocking;)

  8. Avatar for Henry Cordes
    Henry Cordes December 11th, 2011

    @saeed: I am working on a product that uses a lot of javascript. We write just as much c# unit test as we do javascript unit tests. We do tdd for both.
    I am surprised at the lack of jsunittesting done by ms devs.
    We use mvc nunit and qunit.
    It works for us!

  9. Avatar for Kartik Sehgal
    Kartik Sehgal December 11th, 2011

    Thanks a ton!
    We have been using a lot of client side javascript before we started moving to MVC Razor and it was not very clear how we can continue client side Unit Testing.
    Much needed and appreciated! :)

  10. Avatar for Craig
    Craig December 11th, 2011

    I have just been putting test suites in individual js files and including the lot of them in a single html page. We use RequireJS to manage dependencies to make sure everything gets loaded properly, so we don't have to include scripts under test in the main test runner page, because the tests themselves take care of loading them. It works well. It does require a lot of js to be loaded at once, but it also lets us run all tests in a single view.

  11. Avatar for fschwiet
    fschwiet December 11th, 2011

    @Saeed, testing javascript is common enough, go ahead and look at some of the more popular javascript libraries and you will probably find unit tests.
    Here's a project I worked on which ended up with maybe 800-1000 unit tests: http://allrecipes.com/my/menus/planner.aspx There's lots of client-side behavior, I couldn't have done it without writing tests.

    @Haacked: One downside of this approach is that you have to open multiple pages to verify all your tests. I prefer to have a single test page that references all the tests. I might have multiple test pages to isolate slow tests, or to isolate tests using components like jasmine-ajax that affect all tests once loaded.

    And last but not least, Jasmine > Qunit :) http://pivotal.github.com/jasmine/

  12. Avatar for Daniel15
    Daniel15 December 11th, 2011

    @fschwiet: I'd say JsTestDriver (http://code.google.com/p/js-test-driver/) and Mocha (http://visionmedia.github.com/mocha/) are both better than Jasmine and Qunit. Might be just me though. :)

  13. Avatar for IC
    IC December 11th, 2011

    It's pretty neat but seems to be more useful for testing library type code - i.e. code which provides some standalone functionality (like your Calculator class above). I don't normally write much library code in .js. The main class of code I could test like this is my client-side validators. Most of the .js I write is to affect some change on the DOM - I would still like to be able to unit test it though. What do you suggest for this type of behaviour testing? NB you'll need the real view to which the javascript applies not an isolated footest.cshtml like above.
    @fschwiet I'd be interested to see an example if you can share one. Jasmine looks interesting.

  14. Avatar for Jos&#233; Romaniello
    Jos&#233; Romaniello December 11th, 2011

    I do something like this but instead to run the project as a website.... I use random to build the html test harness (using razor from command line RazorCandle on my github).
    https://github.com/jfromaniello/razorcandle

    Then i run the file with phantomjs... or in the browser.
    The best example of this, is in my Jmail example:
    http://github.com/jfromaniello/jmail
    Jmail is my example of Single Page Web Applicaiton, and the article is here:
    joseoncode.com/...

  15. Avatar for magellings
    magellings December 11th, 2011

    We use regular htm pages for QUnit tests. Tests can then be executed via NUnit with NQUnit and integrated with the build.

  16. Avatar for Tom Janssens
    Tom Janssens December 11th, 2011

    Hey Phil,
    I have actually gone a step further with Razor files, and used them to describe stories for BDD as well with vanilla .NET, so there is no longer a need for a BDD framework; there is an example here: github.com/.../BankAccount.cshtml

  17. Avatar for Andy Cohen
    Andy Cohen December 12th, 2011

    We're heavy on js so I can see the usefulness of this approach. I would still love to see user experience automation (a much more complex task) built in to VS.

  18. Avatar for roger
    roger December 12th, 2011

    So this is basically to unit test your own javascript, right, not to replace selenium which can click on buttons et al?

  19. Avatar for haacked
    haacked December 12th, 2011

    @roger Correct! This is not meant for full browser test automation.

  20. Avatar for Rune
    Rune December 15th, 2011

    Great blogpost... I am eager to get started working test driven on javascript code. Really want to have the same benefits on my client side code as I experience with my c# code. I've seen a couple of different approaches to get to the point when running the test suites from within visual studio. However to gain the true value of the tests I need to be able to get fast feedback from the system and not rely on manual execution. Therefore my real problem arises when I want to build it into the feedback loop using continuous integration. Our biild and ci platform is TFS and i am looking for a way to get the failed tests reported in TFS. Any expience in how to integrate for this purpose?

  21. Avatar for Nicholas
    Nicholas December 15th, 2011

    @haacked: I cannot seem to find the source code to System.Web.Razor.dll in the MVC 3 sources or even Codeplex. I have found a project called RazorEngine but that is RazorEngine.Web.dll, not System.Web.Razor.dll and parser itself. I thought this was open-sourced? Thanks!

  22. Avatar for haacked
    haacked December 15th, 2011

    Razor is not yet open source. Only System.Web.Mvc.dll is open source.

  23. Avatar for Matthew Manela
    Matthew Manela December 20th, 2011

    I am a developer at Microsoft and my team does a lot of JavaScript unit testing. We work on an MVC 3 website and it definitely helps a lot. For a long time we were able to convince ourselves that "it's just view code", but eventually it became impossible to ignore the amount of untested logic.
    My team uses a tool/open source project I created called Chutzpah which executes QUnit or Jasmine unit tests directly from the JS file. It generates the test harness for you when you point it at a js test file. What is nice is that it integrates inside of Visual Studio and can be run from the command line (this lets us make it part of our CI build).