169

Is there a way to easily reset all sinon spys mocks and stubs that will work cleanly with mocha's beforeEach blocks.

I see sandboxing is an option but I do not see how you can use a sandbox for this

beforeEach ->
  sinon.stub some, 'method'
  sinon.stub some, 'mother'

afterEach ->
  # I want to avoid these lines
  some.method.restore()
  some.other.restore()

it 'should call a some method and not other', ->
  some.method()
  assert.called some.method
austinbv
  • 9,297
  • 6
  • 50
  • 82

9 Answers9

326

Sinon provides this functionality through the use of Sandboxes, which can be used a couple ways:

// manually create and restore the sandbox
var sandbox;
beforeEach(function () {
    sandbox = sinon.sandbox.create();
});

afterEach(function () {
    sandbox.restore();
});

it('should restore all mocks stubs and spies between tests', function() {
    sandbox.stub(some, 'method'); // note the use of "sandbox"
}

or

// wrap your test function in sinon.test()
it("should automatically restore all mocks stubs and spies", sinon.test(function() {
    this.stub(some, 'method'); // note the use of "this"
}));
joshweir
  • 5,427
  • 3
  • 39
  • 59
keithjgrant
  • 12,421
  • 6
  • 54
  • 88
  • 7
    @CamJackson When you've got async tests, you need to use the first method, otherwise sinon cleans up its stubs before your test has finished executing. – keithjgrant Sep 30 '14 at 16:49
  • 7
    If you are using sinon >5.0 read below. There's now a much easier method: https://stackoverflow.com/a/55251560/4464702 – randers Jul 05 '19 at 22:42
120

Previous answers suggest using sandboxes to accomplish this, but according to the documentation:

Since sinon@5.0.0, the sinon object is a default sandbox.

That means that cleaning up your stubs/mocks/spies is now as easy as:

var sinon = require('sinon');

it('should do my bidding', function() {
    sinon.stub(some, 'method');
}

afterEach(function () {
    sinon.restore();
});
Myk Willis
  • 12,306
  • 4
  • 45
  • 62
  • 5
    even neeter: afterEach(sinon.restore) – Benjam May 05 '20 at 13:36
  • I think this is better because explicit sandboxes create unnecessary complexity. Are you really going to need several separate sandboxes with different mocks of the same object? Probably not. – Gherman May 06 '20 at 16:46
  • Does it mean there is no difference between sinon.stub(some, 'method'); and const sandbox = sinon.createSandbox(); sandbox.stub(some, 'method'); ? – Téwa Mar 30 '21 at 07:57
  • 1
    I get this message (`sinon.restore is deprecated and will be removed from the public API in a future version of sinon.`) when I use `sinon.restore()` in afterEach hook – Željko Šević Apr 20 '21 at 08:10
  • @ŽeljkoŠević I don't see that warning, and you possibly using an old version of sinon (pre v5.0.1)? – Myk Willis Apr 26 '21 at 14:46
14

An update to @keithjgrant answer.

From version v2.0.0 onwards, the sinon.test method has been moved to a separate sinon-test module. To make the old tests pass you need to configure this extra dependency in each test:

var sinonTest = require('sinon-test');
sinon.test = sinonTest.configureTest(sinon);

Alternatively, you do without sinon-test and use sandboxes:

var sandbox = sinon.sandbox.create();

afterEach(function () {
    sandbox.restore();
});

it('should restore all mocks stubs and spies between tests', function() {
    sandbox.stub(some, 'method'); // note the use of "sandbox"
} 
oligofren
  • 20,744
  • 16
  • 93
  • 180
anand
  • 695
  • 9
  • 21
9

You may use sinon.collection as illustrated in this blog post (dated May 2010) by the author of the sinon library.

The sinon.collection api has changed and a way to use it is the following:

beforeEach(function () {
  fakes = sinon.collection;
});

afterEach(function () {
  fakes.restore();
});

it('should restore all mocks stubs and spies between tests', function() {
  stub = fakes.stub(window, 'someFunction');
}
Dimitris Zorbas
  • 5,187
  • 3
  • 27
  • 33
8

restore() just restores the behavior of the stubbed functionality but it doesn't reset the state of the stubs. You'll have to either wrap your tests with sinon.test and use this.stub or individually call reset() on the stubs

Sandeep Adi
  • 91
  • 1
  • 5
6

If you want a setup that will have sinon always reset itself for all tests:

in helper.js:

import sinon from 'sinon'

var sandbox;

beforeEach(function() {
    this.sinon = sandbox = sinon.sandbox.create();
});

afterEach(function() {
    sandbox.restore();
});

Then, in your test:

it("some test", function() {
    this.sinon.stub(obj, 'hi').returns(null)
})
sethcall
  • 2,837
  • 1
  • 19
  • 22
3

Note that when using qunit instead of mocha, you need to wrap these in a module, e.g.

module("module name"
{
    //For QUnit2 use
    beforeEach: function() {
    //For QUnit1 use
    setup: function () {
      fakes = sinon.collection;
    },

    //For QUnit2 use
    afterEach: function() {
    //For QUnit1 use
    teardown: function () {
      fakes.restore();
    }
});

test("should restore all mocks stubs and spies between tests", function() {
      stub = fakes.stub(window, 'someFunction');
    }
);
sfuqua
  • 5,797
  • 1
  • 32
  • 33
2

Create a sandbox which will act as a black box container for all your spies, stubs, mocks and fakes.

All you have to do is create a sandbox in the very first describe block so that, it is accessible throughout all the test cases. And once you are done with all the test cases you should release the original methods and clean up the stubs using the method sandbox.restore() in the afterEach hook so that at runtime it releases held up resources afterEach test case is passed or failed.

Here is an example:

 describe('MyController', () => {
    //Creates a new sandbox object
    const sandbox = sinon.createSandbox();
    let myControllerInstance: MyController;

    let loginStub: sinon.SinonStub;
    beforeEach(async () => {
        let config = {key: 'value'};
        myControllerInstance = new MyController(config);
        loginStub = sandbox.stub(ThirdPartyModule, 'login').resolves({success: true});
    });
    describe('MyControllerMethod1', () => {
        it('should run successfully', async () => {
            loginStub.withArgs({username: 'Test', password: 'Test'}).resolves();
            let ret = await myControllerInstance.run();
            expect(ret.status).to.eq('200');
            expect(loginStub.called).to.be.true;
        });
    });
    afterEach(async () => {
        //clean and release the original methods afterEach test case at runtime
        sandbox.restore(); 
    });
});
kavigun
  • 2,219
  • 2
  • 14
  • 33
1

below will reset all the stubs and nested stubs.

sinon.reset();

or you do NameOfFunctiontionYouWantToReset.resetHistory();

like addingStub.resetHistory();

Arian Al Lami
  • 867
  • 7
  • 9