nicangeli nicangeli - 6 months ago 96
Node.js Question

Unit testing multiple asynchronous calls that return promises with Mocha

I'm trying to understand how to best unit test my asynchronous CommonJS modules. I'm struggling to understand best practice when dealing with multiple chained promises.

Lets assume I have the following module defined:

module.exports = function(api, logger) {
return api.get('/foo')
.then(res => {
return api.post('/bar/' + res.id)
})
.then(res => {
logger.log(res)
})
.catch(err => {
logger.error(err)
})
}


and I have the following spec file trying to test that the correct calls are made.

var module = require('./module')
describe('module()', function() {
var api;
var logger;
var getStub;
var postStub;
beforeEach(function() {
getStub = sinon.stub();
postStub = sinon.stub();
api = {
get: getStub.resolves({id: '123'),
post: postStub.resolves()
}
logger = {
log: sinon.spy(),
error: sinon.spy()
}
})
afterEach(function() {
getStub.restore();
postStub.restore();
});
it('should call get and post', function(done) {
module(api, logger) // System under test
expect(getStub).to.have.been.calledWith('/foo')
expect(postStub).to.have.been.calledWith('/bar/123')
done()
})
})


This doesn't work. The first assertion passes correctly, but the second assertion fails, as presumably the promise hasn't resolved at the time of execution.

I can fix this using process.nextTick or setTimeout but I'd like to see how other people have solved this more gracefully.

I've tried adding chai-as-promised into the mix with little luck. My current setup includes, sinon, chai, sinon-as-promised and sinon-chai.

Thanks

Answer

You should use the fact that module() returns a promise, so you can add another .then() to the chain where you can assert the arguments (because at that point the previous .then() steps have been called, including the call to api.post()).

And since Mocha supports promises, you can return the resulting promise instead of having to deal with done:

it('should call get and post', function() {
  return module(api, logger).then(() => {
    expect(getStub).to.have.been.calledWith('/foo')
    expect(postStub).to.have.been.calledWith('/bar/123')
  });
})