lawrencec / hugs

Wrapper around common testing frameworks in order to use a common API in unit tests
1 stars 1 forks source link

Hugs Build Status Code Climate Test Coverage Latest Release

Boy hugging piñata

Are you fatigued by any of the following?

If you are, then you need Hugs!

Hugs is a module that allows you to write your nodejs tests in exactly the same way regardless of which test framework you choose to use. It does this by wrapping those frameworks with a consistent API.

Currently, Hugs supports the following test frameworks:

Sinon spies, stubs and mocks are created using sinon's sandbox before every test (and torn down afterwards).

Hugs has only one opinion and that is that C-style asserts should be used for assertions.

assert.calledWith(anObject.aMethod, 'foo');

is better than

expect(anObject.aMethod).to.have.been.calledWith("foo");

Less typing means more time to hold the hand of your favourite person/pet/piñata instead!

However, if you really want to type more things, then you still can as Hugs does not mandate you have to use asserts.

Why?

Working on multiple projects and multiple teams, each with a different preference for a test runner (Jasmine or Mocha or TAP) and assertion framework (assert, expect or should) as well as test double frameworks (Jasmine or sinon), I got fatigued off with trying to remember which methods I should be calling. All I want to do is set up a test and check that a value is equal to something. I was also bored with every new project I start to have to set up my test setup again. With hugs, I only need to install hugs and my testrunner e.g mocha.

Install

npm install hugs

Usage

Below is an example snippet but see /examples folder. It needs to be run with the corresponding test runner for the chosen framework.

$> cd examples
$> npm install
$> npm run examples # runs examples under both mocha and tap
$> npm run example:mocha # just run mocha example
$> npm run example:tap # just run tap example
$> npm run example:browser:mocha # just run browser mocha example
$> npm run example:browser:jasmine # just run browser jasmine example
$> npm run help # see all available npm run commands
let hugs = require('hugs');

// This example uses Mocha but the require call can be changed to 'tap'
// and without any other changes, the tests will work

let test = hugs(require('mocha'));

let spy = test.spy;
let assert = require('assert');

let oUT = { method: () => { } };

test(
  'a test with a spy',
  function (done) {
    spy(oUT, 'method');

    oUT.method(1, 2);

    assert.callCount(oUT.method, 1); // sinon assertions exposed onto native asserts object
    assert.calledWith(oUT.method, 1, 2);

    done();
  }
);

More examples can be seen in the /examples directory

Browser tests using Karma

There isn't a karma framework for hugs as it is only a wrapper around testing frameworks. In order to configure karma to use hugs, configure karma as if you were using your framework of choice e.g mocha and add the following lines to your files section before your tests files like so:

files: [
  'index.js', // thing you are testing
  'node_modules/hugs/src/exporters/sinon.js', // add this
  'node_modules/hugs/src/targets/mocha.js', // add this and replace with tape or ava etc
  'node_modules/hugs/src/index.js',  // add this
  './test/index.js' // your test files.
],

See the /examples directory for a working example of a karma setup.

Ending tests

Different frameworks have slightly different mechanisms for ending tests with asynchronous code.

In order to have a test be written in the same way regardless of the test runner used, the done() method ends the test correctly depending on the given runner, calling either t.end(), done() or returning a promise. For mocha users, this means that all tests need to be written as if it's an async test i.e done function is a parameter to the test. The --async-only flag for mocha cli is useful here. AVA uses a different function altogether for async tests called test.cb. This meant adding a cb() function to the hugs interface which when using mocha or tap is actually the same method as their respective test() method.

Type Lifecycle async (callback) async (promise)
AVA signature test.cb(’title’, (t) => {}); test(‘title’, () => {});
AVA end t.end(); return promise;
Mocha signature test(’title’, (done) => {}); test(‘title’, () => {});
Mocha end done(); return promise;
Tap signature test(’title’, (t) => {}); test(‘title’, (t) => {});
Tap end t.end(); return promise;
Hugs signature test.cb('title', (done) => {}); test('title', () => {});
Hugs end done(); return promise;

API

The test object returned from a hugs() call has the following API:

Tests

To run unit tests:

npm run test:unit

To run functional, mocha and tap (node), as well as mocha and jasmine (browser) tests:

npm run test:functional

To run all browser functional tests only:

npm run test:browser

More options available; see npm run help

Some things you need to know about hugs