Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 698
nicangeli Avatar asked Nov 09 '22 14:11

nicangeli


1 Answers

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')
  });
})
like image 100
robertklep Avatar answered Nov 14 '22 22:11

robertklep