Javascript test frameworks

JS (Node) Test Frameworks

Assert

  • Built into node.js
assert.fail(actual, expected, message, operator)
assert.ok(value, [message])
assert.equal(actual, expected, [message])
assert.doesNotThrow(block, [error], [message])
assert.ifError(value)

Mocha

  • browser support
  • test coverage reporting
  • simple async support
  • times tests & highlights slow tests
  • global variable leak reporting
  • before, after, before each, after each hooks
  • use any assertion library you want
  • run tests matching regex

Synchronous

describe('Array', function(){
  describe('#indexOf()', function(){
    it('should return -1 when the value is not present', function(){
      [1,2,3].indexOf(5).should.equal(-1);
      [1,2,3].indexOf(0).should.equal(-1);
    })
  })
})

Async

describe('User', function(){
  describe('#save()', function(){
    it('should save without error', function(done){
      var user = new User('Luna');
      user.save(function(err){
        if (err) throw err;
        done();
      });
    })
  })
})

Expresso

  • By TJ Holowaychuk (author of Express web framework)
  • async support
  • test coverage support using node-jscoverage
assert.isNull(null);
assert.isNotNull(false);
assert.isDefined(false);
assert.isUndefined(undefined);
assert.length('foo', 3);
assert.includes([1,2,3], 3);

Define tests by exporting a function

exports['test String#length'] = function(){
    assert.equal(6, 'foobar'.length);
};

For large numbers of tests you can export a “suite”

module.exports = {
    'test String#length': function(beforeExit, assert) {
      assert.equal(6, 'foobar'.length);
    }
};

Async exports

setTimeout(function() {
    exports['test async exports'] = function(){
        assert.ok('wahoo');
    };
}, 100);

Nodeunit

  • Runs in browser or node
  • async support
  • setup & teardown functions
  • flexible reporters HTML & XUnit style output
  • allows use of mocks and stubs
exports.testSomething = function(test){
    test.expect(1);
    test.ok(true, "this assertion should pass");
    test.done();
};

exports.testSomethingElse = function(test){
    test.ok(false, "this assertion should fail");
    test.done();
};

Jasmine

  • BDD Framework
  • can be run in browser, or node
  • Ruby Gem for rails testing
  • From Pivotal Labs (makers of Pivotal Tracker)
describe("Jasmine", function() {
  it("makes testing JavaScript awesome!", function() {
    expect(yourCode).toBeLotsBetter();
  });
});

Vows

  • async BDD framework for node.js
  • executes tests in parallel
  • executes tests sequentially when there are dependencies
  • emphasis on speed of execution, clarity, and user experience
var vows = require('vows'),
    assert = require('assert');

// Create a Test Suite
vows.describe('Division by Zero').addBatch({
    'when dividing a number by zero': {
        topic: function () { return 42 / 0 },

        'we get Infinity': function (topic) {
            assert.equal (topic, Infinity);
        }
    },
    'but when dividing zero by zero': {
        topic: function () { return 0 / 0 },

        'we get a value which': {
            'is not a number': function (topic) {
                assert.isNaN (topic);
            },
            'is not equal to itself': function (topic) {
                assert.notEqual (topic, topic);
            }
        }
    }
}).run(); // Run it

Kyuri

  • A node.js cucumber implementation
  • a few extra asynchronous keywords.
  • supports 160+ languages
  • exports to VowsJS stub

Whiskey

  • From Rackspace
  • text, html, json reporters (no xunit?)
  • support for hooking up the coverage for services started with the process runner
  • support for aggregating code coverage reports
  • code coverage
  • test isolation — each test runs in a separate process
  • needs unix sockets?
  • process runner — keeps track of integration test dependencies via json config file

should
Assertion framework

</pre>
var user = {
    name: 'tj'
  , pets: ['tobi', 'loki', 'jane', 'bandit']
};

user.should.have.property('name', 'tj');
user.should.have.property('pets').with.lengthOf(4);

someAsyncTask(foo, function(err, result){
  should.not.exist(err);
  should.exist(result);
  result.bar.should.equal(foo);
});

Gently
for mocking

See also this list:
https://github.com/joyent/node/wiki/modules#testing

javascript code coverage tools

http://siliconforks.com/jscoverage/

shows what lines of a program have been executed
and which have been missed
this information is useful for constructing comprehensive test suites
code coverage statistics are collected while JS is executed in a browser

http://jescov.olabini.com/

Find line and branch coverage
Currently integrates with jasmine
Is a Java Project
Requires you to run code with Rhino
uses Rhino debugger interface to hook into loading source files
Format can be exported to Cobertura style XML or HTML report

http://code.google.com/p/script-cover/

Chrome Extension
Line by Line JS Code Coverage statistics
Reports which statements from internal/external scripts have been executed
Counts how many times
In browser doesn’t work
It doesn’t check source, but execution

http://hrtimer.mozdev.org/

hrcov
List all scripts Firefox knows about (included in HTML)
Executes in browser?

http://coveraje.github.com/

written in javascript
uses uglifyjs parser/mangler/compressor/beautifier
for js programs executed in node
proxy to execute JS in browser in the works

https://github.com/chrisdickinson/node-runforcover

uses node-bunker
provides code coverage data for your unit test library (whatever that may be)

var runforcover = require('runforcover');
var coverage = runforcover.cover(/.*/g);
coverage(function(coverageData) {
    // coverageData is an object keyed by filename.
    var stats = coverageData['/full/path/to/file.js'].stats()

    // the percentage of lines run versus total lines in file
    console.log(stats.percentage);

    // the number of missing lines
    console.log(stats.missing);

    // the number of lines run (seen)
    console.log(stats.seen);

    // an array of line objects representing 'missed' lines
    stats.lines;

    stats.lines.forEach(function(line) {
	// the line number of the line:
	console.log(line.number);

	// returns a string containing the source data for the line:
	console.log(line.source());   
    }); 

    // return control back to the original require function
    coverage.release(); 
});

https://github.com/itay/node-cover.git

fork of runforcover
bugfixes
uses esprima instead of bunker
If you use ‘global’ to pass state between modules (mocha does this, for example), then you might run into issues.
Cover runs modules as if they were executed with NODE_MODULE_CONTEXTS was set.
If you start new node processes, Cover won’t work with those, as it instruments by hooking into require.

See also this blog post:

http://ariya.ofilabs.com/2012/03/javascript-code-coverage-and-esprima.html