140

Though there is a same question here but I could not find answer to my problem so here goes my question:

I am testing my node js app using mocha and chai. I am using sinion to wrap my function.

describe('App Functions', function(){

  let mockObj = sinon.stub(testApp, 'getObj', (dbUrl) => {
     //some stuff
  });
  it('get results',function(done) {
     testApp.someFun
  });
}

describe('App Errors', function(){

  let mockObj = sinon.stub(testApp, 'getObj', (dbUrl) => {
     //some stuff
  });
  it('throws errors',function(done) {
     testApp.someFun
  });
}

When I try to run this test it gives me error

Attempted to wrap getObj which is already wrapped

I also tried putting

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

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

in each describe, but still giving me same error.

Matko
  • 3,386
  • 4
  • 21
  • 35
rovy
  • 2,481
  • 5
  • 18
  • 26
  • You can find an explanation at the bottom of the post [here](https://medium.com/@alfasin/stubbing-with-sinon-4d6539caf365) – Nir Alfasi Jan 26 '19 at 01:48

11 Answers11

148

You should restore the getObj in after() function, please try it as below.

describe('App Functions', function(){
    var mockObj;
    before(function () {
            mockObj = sinon.stub(testApp, 'getObj', () => {
                 console.log('this is sinon test 1111');
            });
    });

    after(function () {
        testApp.getObj.restore(); // Unwraps the spy
    });

    it('get results',function(done) {
        testApp.getObj();
    });
});

describe('App Errors', function(){
    var mockObj;
    before(function () {
            mockObj = sinon.stub(testApp, 'getObj', () => {
                 console.log('this is sinon test 1111');
            });
    });

    after( function () {
        testApp.getObj.restore(); // Unwraps the spy
    });

    it('throws errors',function(done) {
         testApp.getObj();
    });
});

Update 2022/01/22

Using sinon's sanbox you could created stub mocks with sandbox.stub() and restores all fakes created through sandbox.restore(), Arjun Malik give an good example

zangw
  • 43,869
  • 19
  • 177
  • 214
  • After trying the above accepted way, I am getting the same error under "before all" hook – Ashwin Hegde Jul 24 '17 at 05:42
  • @AshwinHegde, could you please give me your test codes? Maybe I can find some issue here. – zangw Jul 24 '17 at 05:49
  • 2
    Is there no way to restore all stubs without specifying each one? Would be great to have a `sinon.restoreAll();` that could be run after all tests just to make sure you don't forget to restore a stub. – Luke Oct 18 '18 at 21:38
  • 1
    afterEach(()=> { sinon.verifyAndRestore(); }); – Sam Arul Raj T Oct 22 '20 at 12:35
  • 1
    Using sinon's sanbox you could created stub with `sandbox.stub(...)` and restore everything later with just `sandbox.restore()` – Rubens Mariuzzo Jan 22 '22 at 21:08
42

This error is due to not restoring the stub function properly. Use sandbox and then create the stub using the sandbox. After each test inside the suite, restore the sandbox

  beforeEach(() => {
      sandbox = sinon.createSandbox();
      mockObj = sandbox.stub(testApp, 'getObj', fake_function)
  });

  afterEach(() => {
      sandbox.restore();
  });
rantao
  • 1,621
  • 4
  • 14
  • 34
Arjun Malik
  • 421
  • 4
  • 5
12

For cases where you need to restore all the methods of one object, you can use the sinon.restore(obj).

Example:

before(() => {
    userRepositoryMock = sinon.stub(userRepository);
});

after(() => {
    sinon.restore(userRepository);
});
Renan Ferreira
  • 2,122
  • 3
  • 21
  • 30
  • 1
    This didn't work for me when stubbing functions on the object. I had to restore per function like the accepted answer shows. – Ian Robertson Jan 13 '17 at 16:45
  • 8
    sinon.restore() was deprecated in Sinon v2 and removed afterwards. `// Previously sinon.restore(stubObject); // Typescript (stubObject as any).restore(); // Javascript stubObject.restore();` – Matthias Sommer Nov 23 '17 at 09:15
  • calling sinon.restore() inside afterEach() solved this issue, sinon version installed is 5.1.1 – Sandoval0992 Oct 28 '22 at 03:56
8

I was also hitting this using the before() and after() hooks of Mocha. I was also using the restore() as mentioned everywhere. Single test file ran fine, multiple did not. Finally found about Mocha root-level-hooks: I did not have my before() and after() inside my own describe(). So it finds all files with before() at the root-level and executes those before starting any tests.

So make sure you have a similar pattern:

describe('my own describe', () => {
  before(() => {
    // setup stub code here
    sinon.stub(myObj, 'myFunc').callsFake(() => {
      return 'bla';
    });
  });
  after(() => {
    myObj.myFunc.restore();
  });
  it('Do some testing now', () => {
    expect(myObj.myFunc()).to.be.equal('bla');
  });
});
Wilfred Dittmer
  • 151
  • 2
  • 5
8

Just a heads-up, because this took me about an hour to figure out:

If you have two (or more) test files, and find yourself still getting "already wrapped" error no matter what you try, make sure your beforeEach and afterEach stub / replace handlers are INSIDE the test file's describe block.

If you put them in the global test scope, i.e. OUTSIDE the describe('my test description', () => {}) construct, sinon will attempt it twice and throw this.

Amc_rtty
  • 3,662
  • 11
  • 48
  • 73
5

For anyone running into this issue, if you stub or spy on the entire object, and you later do

sandbox.restore()

You'll still get the error. You have to stub/spy the individual methods.

I wasted forever trying to figure out what was wrong.

sinon-7.5.0

Khon Lieu
  • 4,295
  • 7
  • 37
  • 39
3

It is advised to initialize stubs in 'beforeEach' and restore them in 'afterEach'. But in case you are feeling adventurous, the following works too.

describe('App Functions', function(){

  let mockObj = sinon.stub(testApp, 'getObj', (dbUrl) => {
     //some stuff
  });
  it('get results',function(done) {
     testApp.someFun
     mockObj .restore();
  });
}

describe('App Errors', function(){

  let mockObj = sinon.stub(testApp, 'getObj', (dbUrl) => {
     //some stuff
  });
  it('throws errors',function(done) {
     testApp.someFun
     mockObj .restore();
  });
}
Karna
  • 579
  • 6
  • 5
3

Even with sandbox it could give you the error. Especially when tests run in parallel for ES6 classes.

const sb = sandbox.create();

before(() => {
  sb.stub(MyObj.prototype, 'myFunc').callsFake(() => {
    return 'whatever';
  });
});
after(() => {
  sb.restore();
});

this could throw the same error if another test is trying to stub myFunc from the Prototype. I was able to fix that but I am not proud of it...

const sb = sandbox.create();

before(() => {
  MyObj.prototype.myFunc = sb.stub().callsFake(() => {
    return 'whatever';
  });
});
after(() => {
  sb.restore();
});
Tonino
  • 1,137
  • 10
  • 25
3

I ran into this with spies. This behaviour makes sinon pretty inflexible to work with. I created a helper function that attempts to remove any existing spy before setting a new one. That way I don't have to worry about any before/after state. A similar approach might work for stubs too.

import sinon, { SinonSpy } from 'sinon';

/**
 * When you set a spy on a method that already had one set in a previous test,
 * sinon throws an "Attempted to wrap [function] which is already wrapped" error
 * rather than replacing the existing spy. This helper function does exactly that.
 *
 * @param {object} obj
 * @param {string} method
 */
export const spy = function spy<T>(obj: T, method: keyof T): SinonSpy {
  // try to remove any existing spy in case it exists
  try {
    // @ts-ignore
    obj[method].restore();
  } catch (e) {
    // noop
  }
  return sinon.spy(obj, method);
};
Phil
  • 180
  • 1
  • 5
0

I met this behavior because the function was spy-ed somewhere else. So, I removed the predefined spy like below and created my own.

obj.func.restore()
let spy = sinon.spy(obj, 'func')

It works.

DevExcite
  • 399
  • 4
  • 6
-1
function stub(obj, method) {
     // try to remove any existing stub in case it exists
      try {
        obj[method].restore();
      } catch (e) {
        // eat it.
      }
      return sinon.stub(obj, method);
    }

and use this function when creating stubs in tests. It will resolve 'Sinon error Attempted to wrap function which is already wrapped' error.

example:

stub(Validator.prototype, 'canGeneratePayment').returns(Promise.resolve({ indent: dTruckIndent }));
gramcha
  • 692
  • 9
  • 16