In case you haven’t heard, Azavea likes to build beautiful and functional web applications. Particularly, geospatial applications. Currently, each app involves a great deal of JavaScript for user interactions and dynamic content. This has presented a unique set of challenges when it came to code quality tools and best practices with respect to unit testing and Test Driven Development (TDD). I’ll address JavaScript unit testing in .NET in this post, since many of our projects use these two technologies.
Unit Testing / TDD
As it turns out, there aren’t a whole lot of great tools in the .NET ecosystem that will unit test JavaScript source files. Sure, you could install nodejs on a windows box, or even run your tests in the browser through QUnit. It really comes down to a matter of taste, and my preference is to have fewer moving pieces in the build chain if possible. It turns out that JSTest.NET fits into our .NET ecosystem nicely, so I wanted to give it a shot (it also helps that it’s a package available in NuGet).
One of the interesting parts about JSTest.NET is that it runs in the Windows Scripting Host, which is a neat way to run JavaScript programs from the command line in Windows. If you’ve ever double-clicked on a JavaScript file, and received a cryptic error, you probably ran the file through the Windows Scripting Host without realizing it. Since the executing process is this scripting host, you don’t have access to normal browser stuff, like “window” or “document”. This turns out to be important, especially in web applications.
JSTest.NET + require.js
Require.js is a great way to write modular JavaScript code in client and server environments. Part of the design of require.js is to enforce an Asynchonous Module Definition (AMD) API style in your JavaScript source. This helps developers in many ways; I’m not here to sing its praises, but it has helped our teams build reliable apps.
Part of AMD’s architectural design means that the browser will asynchronously load JavaScript modules when they are needed, based on the name of the module and an optional URL mapping. It’s up to the engineer’s discretion, but these modules could be quite atomic and have no dependencies (including the global ‘window’ object in every browser). Unit testing libraries that operate outside the browser (and therefore don’t have any real URLs associated with them), seems to throw a wrench in the whole AMD process.
As it turns out, it’s no wrench at all. Require.js loads modules only once, and keeps defined modules around until they are explicitly discarded. With this knowledge, it’s trivial to create a set of tests by manually including module dependencies. It’s best to see this by example. Let’s get started.
Processor Example
Assume I have a JavaScript library module that processes data in some fashion:
[gist id=5007506 file=processor.js]
This module defines itself as ‘processor’. This is not strictly necessary when using require.js, but it is quite important in the require.js loading mechanism (it is the only way to retrieve a module from require.js after it has been loaded). This module also includes the special module id ‘module’ as the only dependency.
To test this module with require, we need a JavaScript “test suite” file, and a C# test file which will be executed by our test runner. This puts all of our code and tests in JavaScript, and the C# source file merely manages the suite setup for all tests in the JavaScript “test suite”. Our “test suite” is pretty simple right now; let’s test that the loading of the module went swimmingly:
[gist id=5007506 file=processor-suite.js]
Wait, what? Why are there two “require” calls in there? The first invocation of require (with an array argument) is the form that registers a module that has previously been declared with “define”. The second invocation of require (with a module name argument) is the form that retrieves the cached module by that same name. You can test this out in any web page that uses require: if you use open up a JavaScript console in the browser, you can use the form “require(‘modulename’)” to get the currently loaded module (that is, if you know the module names that the application developer is using). The two of these calls together effectively 1) register the module, then 2) return the loaded url from require’s cache. Now that we’ve convinced require not to load any modules via XMLHttpRequests, we can start testing our modules.
Next, we need something to pilot the tests: a C# class loads up the scripts and starts the tests:
[gist id=5007506 file=ProcessorTest.cs]
I’ll walk you through what’s happening:
- JSTest loads all the global function calls in the suite on line 12 into a set of NUnit DataPoints.
- NUnit runs the Test method (since it’s a Theory, and not a Test) against all the discovered DataPoint types in the class — in our case, it’s an array of TestCase objects, where each one is the name of a global function to execute.
- JSTest starts composing a TestScript, and includes the JsAssert library on line 20 (this provides the ‘assert’ object)
- We added our own lines below that add the require.js and processor.js JavaScript sources to the TestScript on line 23 and 24
- JSTest then adds the test suite to the TestScript on line 25
- JSTest runs this TestScript.
This process makes it clear that:
- JsAssert is the first JavaScript source loaded.
- Require.js is loaded prior to our modules.
- Our test suite is the last JavaScript source loaded.
- The test method will be run once for each global function found in the test suite.
Processor/Renderer Example
This is all well and good, but what happens when we start adding module dependencies? Let’s say we have a second module, which depends on our processor:
[gist id=5007506 file=renderer.js]
This module loads the ‘processor’ module as a dependency. To test this in JSTest, simply create the “test suite” in JavaScript:
[gist id=5007506 file=renderer-suite.js]
… and create the C# test pilot, and notice that we are including processor.js manually:
[gist id=5007506 file=RendererTest.cs]
It is a little annoying that you need to list all dependencies of an individual module, but we’re lucky to have relatively small numbers of modules dependent on one another — a page may load many modules, but the unit tests are discrete enough that those dependencies aren’t a big pain (yet).
Now that we can start unit testing our JavaScript from NUnit, we’re going to get crazy! But what happens when your module uses something like jQuery? Remember how I mentioned at the beginning of this article that there is no “window” or “document” object? Well, there’s a way to do that, too. Tune in for the next installment, and I’ll demonstrate how you can use a headless browser to test!
(All these code examples are available in gist form, and you are free to start using them to run your own tests.)