Service1:
const { Service2 } = require('../Service2.js');
const func1 = () => {
const result = Service2.func2();
return result;
}
const Service1 = { func1 };
module.exports = { Service1 };
Test for Service1:
Note that I need to test Service1.func1 which internally depends on Service2.func2, hence i would like to stub Service2.func2
const sinon = require('sinon');
const { expect } = require('chai');
const { Service2 } = require('../Service2');
const func2stub = sinon.stub(Service2, 'func2');
// import Service1 after stubbing Service2.func2, so that it uses the stubbed version of func2
const { Service1 } = require('../Service1');
describe('Service1 Tests', () => {
it('should return some result on calling func1', () => {
func2stub.returns(true);
expect(Service1.func1()).to.equal(true);
}
}
This works fine on its own. But If I have another unrelated test file which calls sinon.restore() in its lifecycle, my Service1 tests start calling the actual implementation of Service2.func2 instead of the stub.
// Another test file
...
...
describe('some other tests', () => {
afterEach(() => { sinon.restore(); }
it('should ...', () => {
...
}
}
...
I have debugged this by commenting out the sinon.restore , and by running just the Service1 tests in isolation.
During my debugging, a workaround that I found for this is to move the stubbing from the top level to before lifecycle in the Service1 tests
const sinon = require('sinon');
const { expect } = require('chai');
let Service1;
let func2stub;
describe('Service1 Tests', () => {
before(() => {
const { Service2 } = require('../Service2');
func2stub = sinon.stub(Service2, 'func2');
// import Service1 after stubbing Service2.func2, so that it uses the stubbed version of func2
Service1 = require('../Service1').Service1;
}
it('should return some result on calling func1', () => {
func2stub.returns(true);
expect(Service1.func1()).to.equal(true);
}
}
This works fine, and is not impacted by the sinon.restore in the other file.
Any pointers to understand why this happens and the proper solution would be helpful.
sinon
provides a default sandbox global to the module, which means everything in the default sandbox is restored when sinon.restore()
is called.
It's possible to use sinon.createSandbox()
to create a more granular sandbox relevant to the service, or test suite, or at whatever level you want to restore
.
describe('tests', function(){
let sandbox;
before(function(){
sandbox = sinon.createSandbox({})
})
afterEach(function(){
sandbox.restore()
})
it('should test', function(){
sandbox.spy(x)
})
}
mocha has two stages to a run
Anything outside of a before
/after
/it
runs immediately during the setup phase.
Read test js files to build test run.
Traverse `describe` functions, running the attached callback function.
Schedule `it` `before` `after` functions for test run.
- Read Blah tests
- Read Service1 tests < Service2 stub setup here
- Read Service2 tests
Execute test run
- Run Blah tests < sinon.restore() removes service2 stub.
- Run Service1 tests
- Run Service2 tests
So mocha has a complete picture of what tests can run, before they run.