Testing Mocha - AutolabJS/autolabcli GitHub Wiki

Mocha Testing Framework

Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases.

Installation

Install Mocha as dev-dependency -

$ npm install --save-dev mocha

Install Chai also as dev-dependency -

$ npm install chai

In your editor create a new file named test.js. Import Chai and get the expect function out of it.

const { expect } = require('chai')

This uses ES6 destructuring.

Write your first test case as below:

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

The describe function takes a string for describing the intent of the succeeding test cases as the first argument and a callback function as a second argument. This callback function contains it blocks which contain the actual test cases. The first argument for it is a string which describes what the test case is testing. The second argument is a callback function that contains the steps for testing. Here we are testing the indexof() function using the expect function from Chai library.

Back in terminal run:

$ ./node_modules/mocha/bin/mocha test.js

Mocha will run the test and show that the test is passing -

$ ./node_modules/mocha/bin/mocha test.js

  for Array
    #indexOf()
      ✓ should return -1 when the value is not present


  1 passing (9ms)

Testing Asynchronous Code

Mocha gives us a very simple way to test asynchronous code. All we need to do is pass an optional callback to it and call it when an asynchronous code executes in order to end the test.

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

This can be rewritten as -

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

Here we are passing a callback (named done() ) to the it block which indicates the end of the test when called. We save the user, which is an asynchronous operation and pass a callback which then call done() on success. If done is called with an Error object this indicates that the test has failed.

Working with Promises

Alternately, instead of using the done() callback, you may return a Promise. This is useful if the APIs you are testing return promises instead of taking callbacks:

describe('#find()', function() {
  it('respond with matching records', function() {
    return db.find({ type: 'User' }).should.eventually.have.length(3);
  });
});

This uses Chai-as-Promised library for fluent promise assertions.

Using async / await

If your JS environment supports async / await you can also write asynchronous tests like this:

describe('#find()', function() {
  it('responds with matching records', async function() {
    const users = await db.find({ type: 'User' });
    users.should.have.length(3);
  });
});

Note: It is important to note that the async/wait approach is preferred over the other two and it should be wherever possible. This is because async/await makes the code concise, clean and more readable.

Hooks

With its default “BDD”-style interface, Mocha provides the hooks before(), after(), beforeEach(), and afterEach(). These should be used to set up preconditions and clean up after your tests.

describe('hooks', function() {

  before(function() {
    // runs before all tests in this block
  });

  after(function() {
    // runs after all tests in this block
  });

  beforeEach(function() {
    // runs before each test in this block
  });

  afterEach(function() {
    // runs after each test in this block
  });

  // test cases
});

For example: Suppose we have to stub a lot of functions in each of test-cases, this can be done in a beforeEach() block and the functions can be restored in the afterEach() block.

describe('hooks', function() {
  
  const sandbox, obj;

  before(function() {
    sandbox = sinon.createSandbox();
    obj = new MyObjct();
  });

  beforeEach(function() {
    sandbox.stub(obj, 'method1');
    sandbox.stub(obj, 'method2');
    sandbox.stub(obj, 'method3');
    sandbox.stub(obj, 'method4');
  });

  afterEach(function() {
    sandbox.restore();
  });

  // test cases
});

This example uses sinon stubs which is covered in sinon section.

References