Unit testing D3 code without browser

Test D3 drawing code without running a browser for speed.

Testing is hard, especially if a custom environment is needed. In our case, JavaScript D3 drawing code was wrapped inside Angular directives and it was difficult to effectively test it. Programmers responsible for D3 drawing code were not familiar with AngularJs framework with its own quirks (like digest cycle, etc), and we could not rely on end to end CasperJs tests.

I always likes unit testing JavaScript functions using Nodejs environment. It is fast, reliable, has lots of unit testing frameworks, etc. When looking at testing D3 code I asked myself: can we avoid using a browser (even a headless one, like PhantomJs) and test D3 charting completely using synthetic browser environment emulation, like jsdom nicely wrapped by benv?

Turns out it is possible and is very easy, especially after discoverring browser environment wrapper benv. The unit tests now are very fast, do not depend on 3rd party tools, can be run in parallel, etc. I start with the following 4 areas showing working tests for:

  • DOM manipulation without D3 to get familiar with benv
  • D3 basic test. D3 is a DOM tree manipulation library in its core, as opposed to canvas drawing libraries. This makes it easy to create a synthetic DOM and let D3 manipulate it, checking the results afterwards.
  • D3 user function testing "spies" on a user function passed to D3 code.
  • D3 event testing shows how to trigger a synthetic browser event and check if D3 correctly routes the event to the registered user handler.
  • D3 code load testing shows that we can even test how D3 code is loaded from inside unit tests.
  • D3 dispatch testing tests all but 1 application line of code by using D3 dispatch custom events.

I used mocha for the first example, but later switched to use my own gt QUnit-compatible test runner, mainly to get code coverage by default. The actual test framework choice matters little, so pick the one you prefer.

I will not copy the code samples and tests from the main example repository. Instead the above urls lead to detailed a README and running code samples. All you need to do is clone the repo to local drive, run npm install; npm test and see the results. The unit test code is in files app-test.js or d3-gt-test.js. Just to get a sense of the unit tests, here is the test to make sure the right number of rectangles is created by the D3 function under test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var Q = require('q');
var benv = require('benv');
QUnit.module('d3-drawing.js', {
setup: function () {
var defer = Q.defer();
benv.setup(function () {
benv.expose({
$: benv.require('./bower_components/jquery/dist/jquery.js')
});
window.d3 = benv.require('./bower_components/d3/d3.js');
defer.resolve();
});
return defer.promise;
},
teardown: function () {
benv.teardown();
}
});
QUnit.test('draws bars with same width', function () {
benv.require('./d3-drawing.js');
window.drawBars('body', [5, 10]);
QUnit.equal($('div.bar').length, 2, 'D3 created 2 div bars');
});

The D3 unit tests running under node validate the drawing library, leaving only simple model / directive / service testing of the wrapper Angular code to Karma runner.

Alternatives

I am showing how to test D3 code using the browser emulation under Node. If you would like to use an actual browser, even a headless Phantomjs, see the existing literature: