Cool Blue Cool Blue - 3 months ago 17
Node.js Question

How to get a sinon stub to call another function on nth call

I want to use a sinon stub to asynchronously test an event emitter.
I want the stub to call a callback after it is called.

I thought

stub.yields
was what I want but not. Is there a neat way to do this?

it('asynchronously emits finish after logging is complete', function(done){
const EE = require('events');
const testEmitter = new EE();

var cb = sinon.stub();
cb.calls(completed); // no such method but this is what I need

testEmitter.on('finish', cb.bind(null));

testEmitter.emit('finish');

function completed() {

expect(cb).to.have.been.calledOnce;
expect(cb).to.have.been.calledOn(null);
expect(cb).to.have.exactArgs();

done()
}

});


Currently, I'm doing something like this...

it('asynchronously emits finish', function(done) {
const EE = require('events');
const testEmitter = new EE();
var count = 1;

process.nextTick(() => testEmitter.emit('finish'));

function cb(e) {
var self = this;
expect(e).to.be.an('undefined');
expect(self).to.equal(testEmitter);
if(!count--)
done()
}

testEmitter.on('finish', cb);

process.nextTick(() => testEmitter.emit('finish'));

});


And it works fine but, I need to generalise it and I thought I could do it more efficiently with sinon. But I can't figure out how to do it from the sinon docs . Am I missing something?




Thanks to Robert Klep, here is the solution...

it('asynchronously emits finish after logging is complete', function(done){
const EE = require('events');
const testEmitter = new EE();

var cb = sinon.spy(completed);

testEmitter.on('finish', cb.bind(null));

testEmitter.emit('finish');

function completed() {

expect(cb).to.have.been.calledOnce;
expect(cb).to.have.been.calledOn(null);
expect(cb).to.have.been.calledWithExactly();

done()
}

});

Answer

You can use a spy, because spies will call the function they are spying on:

var cb = sinon.spy(completed);

If, however, for some reason the event handler never gets called, the test will fail by timing out.