Hello.
I met some strange sinon behaviour with stubs and spies.
I will try to present you my test case.
For example I have a simple module (my-module.js) with two public functions.
It is important. Function foo calls another public function bar.
module.exports = {foo, bar};
function foo() {
console.log('foo');
return bar();
}
function bar() {
console.log('bar');
}
Alson I have a simple unit test for this module. I want to test that bar was called on foo call.
const sinon = require('sinon');
const chai = require('chai');
const myModule = require('./my-module');
chai.use(require('sinon-chai'));
const expect = chai.expect;
describe('test', () => {
const sandbox = sinon.createSandbox();
afterEach(() => sandbox.restore());
it('simple test', () => {
sandbox.stub(myModule, 'bar');
myModule.foo();
expect(myModule.bar).to.be.calledOnce;
});
});
mocha, chai (expect), sinon-chai, chai-as-promisedWhat did you expect to happen?
In my opinion this test should be passed
What actually happens
Test fails on assertion. bar - is true sinon stub but it was not called any time.
How to reproduce
Make simple project with given files and run test
Really long code sample or stacktrace
AssertionError: expected bar to have been called exactly once, but it was called 0 times
This is not so strange. JS ES6 Syntax in your export is fooling you. By stubbing the bar export you overwrite the exported symbol, but not the internal function reference which is bound by a closure in foo. Sinon can't do this. You would need DI or proxyquire to accomplish this.
Had the same problem, the solution is to avoid using function and export const instead.
Like so:
export const myFunc = () => {}
// Or with old CommonJS syntax
exports.myFunc = () => {}
The difference is that when you define function myFunc () {} it becomes encapsulated inside of module function (that wraps the module internally when using Node or after build using WebPack). But when you do export const myFunc = then you just add a property to a module object, which can be mocked.
Also when you do export function myFunc ... it will possibly lead to same problem if you are using Babel/TypeScript, because it will still be compiled to same function myFunc ...; export myFunc
Man, this is frustratingly subtle behavior when working with Typescript. I just ran across this, and it adds to the list of constant reminders that transpilation is perhaps, a bit too magical!
How can I export each public method with export const? if this is the case in typescript
We are trying to keep the GitHub issues list tidy and focused on bugs and feature discussions. The last question is a usage question; please post it to StackOverflow and tag it with sinon, so the bigger community can help answer your questions.
Most helpful comment
This is not so strange. JS ES6 Syntax in your export is fooling you. By stubbing the bar export you overwrite the exported symbol, but not the internal function reference which is bound by a closure in foo. Sinon can't do this. You would need DI or proxyquire to accomplish this.