Sinon error Attempted to wrap function which is already wrapped

Viewed 93913

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.

11 Answers

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();
  });

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

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.

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();
  });
}

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();
});

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);
};

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.

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 }));
Related