Introduction to Unit.js

Philosophy of Unit.js: modern, flexible, simple and intuitive.

Unit.js is an assertion library for Javascript, running on Node.js and the browser. It works with any test runner and unit testing framework like Mocha, Jasmine, Karma, protractor (E2E test framework for Angular apps), QUnit, ... and more.

Unit.js provides an awesome API to write your unit tests in the way you prefer:

  • + Unit.js (fluent style inpired by Atoum)
  • + Assert (of Node.js)
  • + Must.js
  • + Should.js
  • + Sinon.js
  • + other friendly features.

Probably you use already one or more of these libraries :)

To keep simple this documentation, all examples considers that the runner is Mocha (except the examples related to other test runners).

Unit.js supports dependency injection and is extensible via a plugins system easy to use.

The learning curve to be productive with Unit.js is very short. The list of assertions is fully documented in the API doc and assertions are expressive like:

test.string('hello');
test.object(user).hasProperty('email');
test.array(fruit).hasValue('kiwi');

Unit.js was designed to provide the essential tools for writing unit tests with fun and qualities.

Several styles

Basic

var obj = {
  message : 'hello',
  name    : 'Nico'
};

var str = 'Hello world';

// Structure of a request object.
// By example, provided by Express framework and other modules.
var req = {
  headers: {
    'content-type': 'application/json'
  }
};

test.object(obj).hasProperty('name');
test.object(obj).hasProperty('message', 'hello');

test.string(str).startsWith('Hello');
test.string(str).contains('world');
test.string(str).match(/[a-zA-Z]/);

test.value(req).hasHeader('content-type');

test.value(req).hasHeader('content-type', 'application/json');
// or
test.value(req).hasHeaderJson();

Fluent

var obj = {message: 'hello', name: 'Nico'};

var str = 'Hello world';

// Structure of a request object.
// By example, provided by Express framework and other modules.
var req = {
  headers: {
    'content-type': 'application/json'
  }
};

test
  .object(obj)
    .hasProperty('name')
    .hasProperty('message', 'hello')

  .string(str)
    .startsWith('Hello')
    .contains('world')
    .match(/[a-zA-Z]/)

  .value(req)
    .hasHeader('content-type')
    // more precise
    .hasHeader('content-type', 'application/json')
    // or shortcut
    .hasHeaderJson()
;

Expressive

var obj = {message: 'hello', name: 'Nico'};

var trigger = function(){
  throw new Error('Whoops !');
};

test
  .object(obj)
    .hasProperty('name')
    .hasProperty('message', 'hello')

  .when('Add "job" property', obj.job = 'developper')

  .then(function(){
    test.object(obj).hasProperty('job', 'developper');
  })

  .case('Delete all properties', function() {

    test
      .when(function() {
        delete obj.name;
        delete obj.message;
        delete obj.job;
      })

      .then(function(){
        test.object(obj).isEmpty();
      })
    ;
  })

  .if(obj.message = 'Hello world !')
  .and(obj.name = 'say hello')

  .string(obj.message)
    .startsWith('Hello')
    .endsWith('world !')
    .notContains('foobar')

  .given(obj = new Date(2014, 02))

  .date(obj)
    .isBefore(new Date(2020, 12))
    .isAfter(new Date(2010, 01))
    .isBetween(new Date(2014, 01), new Date(2014, 12))

  .then('Test the exception')

  .exception(trigger)
    .hasMessage('Whoops !')
    .hasMessage(/whoops/i)
    .isInstanceOf(Error)

  // or
  .error(trigger)
    .hasMessage('Whoops !')
    .hasMessage(/whoops/i)
;

Integrates the great assertions libraries

Assert

// test 'string' type
test.assert(typeof 'foobar' == 'string');

// then that actual value '==' expected value
test.assert.equal('foobar', 'foobar');

// then that actual value '===' expected value
test.assert.strictEqual('foobar', 'foobar');

// this shortcut works also like this
var assert = test.assert;

// test 'string' type
assert(typeof 'foobar' == 'string');

// then that actual value '==' expected value
assert.equal('foobar', 'foobar');

// then that actual value '===' expected value
assert.strictEqual('foobar', 'foobar');

See the assert tutorial.

Must.js

// test 'string' type
test.must('foobar').be.a.string();

// then that actual value '==' expected value
test.must('foobar' == 'foobar').be.true();

// then that actual value '===' expected value
test.must('foobar').be.equal('foobar');

// Must.js library (alternative style)
var must = test.must;

// test 'string' type
('foobar').must.be.a.string();

// then that actual value '==' expected value
('foobar' == 'foobar').must.be.true();

// then that actual value '===' expected value
('foobar').must.be.equal('foobar');

// this shortcut works also like this

// test 'string' type
must('foobar').be.a.string();

// then that actual value '==' expected value
must('foobar' == 'foobar').be.true();

// then that actual value '===' expected value
must('foobar').be.equal('foobar');

See the Must.js tutorial.

Should.js

// test 'string' type
test.should('foobar').be.type('string');

// then that actual value '==' expected value
test.should('foobar' == 'foobar').be.ok;

// then that actual value '===' expected value
test.should('foobar').be.equal('foobar');

// Should.js library (alternative style)
var should = test.should;

// test 'string' type
('foobar').should.be.type('string');

// then that actual value '==' expected value
('foobar' == 'foobar').should.be.ok;

// then that actual value '===' expected value
('foobar').should.be.equal('foobar');

// this shortcut works also like this

// test 'string' type
should('foobar').be.type('string');

// then that actual value '==' expected value
should('foobar' == 'foobar').be.ok;

// then that actual value '===' expected value
should('foobar').be.equal('foobar');

See the Should.js tutorial.

Sinon.js

Spy:

var spy = test.spy();

spy(42);

test.assert(spy.withArgs(42).calledOnce);

Mock :

function once(fn) {
  var
    returnValue,
    called = false
  ;

  return function () {
    if (!called) {
      called = true;
      returnValue = fn.apply(this, arguments);
    }
    return returnValue;
  };
};

var myAPI = { method: function () {} };
var mock = test.mock(myAPI);

mock
  .expects('method')
  .once()
  .returns(42)
;

var proxy = once(myAPI.method);

test.number(proxy()).is(42);

mock.verify();

Sandbox :

it('test using sandbox', test.sinon.test(function () {

  var myAPI = {
    method: function () {}
  };

  var mockMyApi = this.mock(myAPI).expects('method').once().returns(42);

  var proxy = once(myAPI.method);

  test.number(proxy()).isIdenticalTo(42);

  mockMyApi.verify();
}));

See the Sinon.js tutorial.

Fully documented

The API of Unit.js is fanatically documented with examples for all assertions.

  • API doc : the API of all asserters.
  • spec : the spec of all asserters.
  • guide : several tutorials to learn the unit testing with Unit.js.

Unit.js Quickstart

Community

License

Unit.js is released under a Lesser GNU Affero General Public License, which in summary means:

  • You can use this program for no cost.
  • You can use this program for both personal and commercial reasons.
  • You do not have to share your own program's code which uses this program.
  • You have to share modifications (e.g bug-fixes) you've made to this program.

For more convoluted language, see the LICENSE.

Author

Unit.js is designed and built with love by Nicolas Tallefourtane.