Headless Javascript testing with Jasmine 2.0


Update: Rich Trott was so kind to contribute a patch to ensure test runs terminate for big suites, and that the runner returns a meaningful exit code for use in CI integrations. Be sure to check the repo to get the latest changes.


Published by Lorenzo Planas on March 2, 2013

I have been updating my Javascript projects to use Jasmine 2.0, noticing it includes an improved console reporter. It certainly works better in headless setups using PhantomJS, but you'll need to tweak your configuration to have it working correctly.

First, you'll need to initialize the ConsoleReporter, passing the print() function that Jasmine will use to report the results:

if (navigator.userAgent.indexOf("PhantomJS") > 0) {
  var consoleReporter = new jasmineRequire.ConsoleReporter()({
    showColors: true,
    timer: new jasmine.Timer,
    print: function() {
      console.log.apply(console, arguments)
    }
  });

  jasmine.getEnv().addReporter(consoleReporter);
}

In this case, we just call console.log(). The call will trigger an event we can capture in the PhantomJS runner, and then print the message to the standard ouput. I got the right syntax to initialize the ConsoleReporter from this question on StackOverflow

To run the suite with PhantomJS, I used the sample Jasmine runner from PhantomJS, removing some code we won't need with the new console reporter.

var system = require('system');

if (system.args.length !== 2) {
    console.log('Usage: run-jasmine.js URL');
    phantom.exit(1);
}

// Check for console message indicating jasmine is finished running
var doneRegEx = /^\d+ specs, (\d+) failure/;
var noReallyDoneRegEx = /^Finished in \d[\d\.]* second/;
var rc;

var page = require('webpage').create();

// Route "console.log()" calls from within the Page context
// to the main Phantom context (i.e. current "this")

page.onConsoleMessage = function (msg) {
    system.stdout.write(msg);
    var match = doneRegEx.exec(msg);
    if (match) {
        rc = match[1]==="0" ? 0 : 1;
        return;
    }
    match = noReallyDoneRegEx.exec(msg);
    if (match) {
        system.stdout.writeLine("");
        phantom.exit(rc);
    }
};

system.stdout.writeLine("");
page.open(system.args[1], function(status){
    if (status !== "success") {
        console.log("Couldn't load the page");
    }
    system.stdout.writeLine("");
});

Notice that to print each message coming from Jasmine's console.log() calls, this PhantomJS runner uses system.stdout.write(), instead of further console.log() calls. This will avoid extra new lines in the output. To access the standard output this way you will need PhantomJS 1.9+ -- otherwise, you'll have to stick with console.log() and suffer those extra new lines.

With this setup we can now run PhantomJS, passing as arguments the Jasmine runner and the HTML page where the target code, specs and ConsoleReporter configuration is wired up:

Running Jasmine tests in the console

PhantomJS will print a nice test output in the console. I've noticed PhantomJS reports an unwieldy stack trace for failing tests -- I need to investigate further and reduce the trace to the most meaningful lines.

With this configuration we can also run the same test suite through Jasmine's default HTML interface:

Running Jasmine tests in the browser

Check the full sample setup on GitHub. It uses the same sample files included in Jasmine's standalone distribution. It also includes the necessary changes to run the tests both through the HTML interface and the headless / command-line runner.

If you want to receive more updates related to Javascript programming and testing, follow me on Twitter.

You may find these other posts interesting: