JavaScript Unit Testing Promises with Mocha, Sinon, Chai and Proxyquire


Example

Here we have a simple class to be tested that returns a Promise based on the results of an external ResponseProcessor that takes time to execute.

For simplicty we'll assume that the processResponse method won't ever fail.

import {processResponse} from '../utils/response_processor';

const ping = () => {
  return new Promise((resolve, _reject) => {
    const response = processResponse(data);
    resolve(response);
  });
}

module.exports = ping;

To test this we can leverage the following tools.

  1. mocha
  2. chai
  3. sinon
  4. proxyquire
  5. chai-as-promised

I use the following test script in my package.json file.

"test": "NODE_ENV=test mocha --compilers js:babel-core/register --require ./test/unit/test_helper.js  --recursive test/**/*_spec.js"

This allows me to use es6 syntax. It references a test_helper that will look like

import chai from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import chaiAsPromised from 'chai-as-promised';
import sinonStubPromise from 'sinon-stub-promise';

chai.use(sinonChai);
chai.use(chaiAsPromised);
sinonStubPromise(sinon);

Proxyquire allows us to inject our own stub in the place of the external ResponseProcessor. We can then use sinon to spy on that stub's methods. We use the extensions to chai that chai-as-promised injects to check that the ping() method's promise is fullfilled, and that it eventually returns the required response.

import {expect}       from 'chai';
import sinon          from 'sinon';
import proxyquire     from 'proxyquire';

let formattingStub = {
  wrapResponse: () => {}
}

let ping = proxyquire('../../../src/api/ping', {
  '../utils/formatting': formattingStub
});

describe('ping', () => {
  let wrapResponseSpy, pingResult;
  const response = 'some response';

  beforeEach(() => {
    wrapResponseSpy = sinon.stub(formattingStub, 'wrapResponse').returns(response);
    pingResult = ping();
  })

  afterEach(() => {
    formattingStub.wrapResponse.restore();
  })

  it('returns a fullfilled promise', () => {
    expect(pingResult).to.be.fulfilled;
  })

  it('eventually returns the correct response', () => {
    expect(pingResult).to.eventually.equal(response);
  })
});

Now instead let's assume you wish to test something that uses the response from ping.

import {ping} from './ping';

const pingWrapper = () => {
  ping.then((response) => {
    // do something with the response
  });
}

module.exports = pingWrapper;

To test the pingWrapper we leverage

  1. sinon
  2. proxyquire
  3. sinon-stub-promise

As before, Proxyquire allows us to inject our own stub in the place of the external dependency, in this case the ping method we tested previously. We can then use sinon to spy on that stub's methods and leverage sinon-stub-promise to allow us to returnsPromise. This promise can then be resolved or rejected as we wish in the test, in order to test the wrapper's response to that.

import {expect}   from 'chai';
import sinon      from 'sinon';
import proxyquire from 'proxyquire';

let pingStub = {
  ping: () => {}
};

let pingWrapper = proxyquire('../src/pingWrapper', {
  './ping': pingStub
});

describe('pingWrapper', () => {
  let pingSpy;
  const response = 'some response';

  beforeEach(() => {
    pingSpy = sinon.stub(pingStub, 'ping').returnsPromise();
    pingSpy.resolves(response);
    pingWrapper();
  });

  afterEach(() => {
    pingStub.wrapResponse.restore();
  });

  it('wraps the ping', () => {
    expect(pingSpy).to.have.been.calledWith(response);
  });
});