Unit Testing / TDD
JSTest.NET + require.js
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.
[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.
[gist id=5007506 file=processor-suite.js]
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)
- JSTest then adds the test suite to the TestScript on line 25
- JSTest runs this TestScript.
This process makes it clear that:
- Require.js is loaded prior to our modules.
- The test method will be run once for each global function found in the test suite.
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]
[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).
(All these code examples are available in gist form, and you are free to start using them to run your own tests.)