Hello, I’m having trouble with sinon.spy, I’m using meteortesting:mocha/chai/sinon when I try to write a test to check if a nested function was invoked it always returns 0
const a = function () {
b();
}
const b = function () {
console.log('on b function');
}
// export both a and b so I can import { a } from 'a.js'; import { b } from 'b.js';
it('Should have called b', function () {
const spyB = sinon.spy(b);
a();
console.log(spyB.callCount); // returns 0
sinon.assert.calledOnce(spyB);
});
// test fails as it nevers records the call to b() even though I see the console log that it was invoked
Is there something I missed? hope the example makes sense.
In your example a is still using b directly. The spy can’t replace the variable reference that a already holds in a separate module. This is partially because imported values are immutable, and partially because const.
The easiest way around this is to have every function belong to a namespace / object / static class because properties of an object can be changed:
const NS = {}
NS.a = function () {
NS.b();
}
NS.b = function () {
console.log('on b function');
}
// export NS, which can be destructured into a and b
import NS from './ns.js';
it('Should have called b', function () {
const spyB = sinon.spy(NS, 'b');
NS.a();
console.log(spyB.callCount); // returns 1
sinon.assert.calledOnce(spyB);
spyB.restore() // remove spy from method
});
Thanks for your detailed explanation @coagmano, makes a lot of sense that due immutability I can’t replace those variable references with sinon.spy, Will try out your approach!
const a = function () {
b();
}
const b = function () {
console.log('on b function');
}
// export both a and b so I can import { a } from 'a.js'; import { b } from 'b.js';
// on a.test.js
import { a } from './a.js';
import { rewire$b, restore } from './b.js';
it('Should have called b', function () {
let spyB;
rewire$b(spyB = sinon.spy());
a();
console.log(spyB.callCount); // returns 1
sinon.assert.calledOnce(spyB); // Passes!!
restore();
});