Sinon错误尝试包装已经包装的函数

时间:2016-03-18 01:35:18

标签: node.js sinon

虽然这里有同样的问题,但我找不到问题的答案,所以这里提出了我的问题:

我正在使用mocha和chai测试我的节点js app。我正在使用sinion来包装我的功能。

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

当我尝试运行此测试时,它会给我错误

Attempted to wrap getDbObj which is already wrapped

我也尝试过把

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

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

在每个描述中,但仍然给我同样的错误。

10 个答案:

答案 0 :(得分:74)

您应该恢复getObj功能中的after(),请按以下方式尝试。

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

答案 1 :(得分:9)

对于需要恢复一个对象的所有方法的情况,可以使用sinon.restore(obj)

示例:

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

after(() => {
    sinon.restore(userRepository);
});

答案 2 :(得分:5)

我也是使用Mocha的before()和after()钩子来进行此操作。我也在使用各处提到的restore()。单个测试文件运行正常,多个没有。 最后发现了Mocha root-level-hooks:我在自己的describe()中没有my()和after()。因此它在根级别找到所有带有before()的文件,并在开始任何测试之前执行这些文件。

因此,请确保您有类似的模式:

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

答案 3 :(得分:2)

建议在'beforeEach'中初始化存根,并在'afterEach'中恢复它们。但如果您有冒险精神,以下情况也会奏效。

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

答案 4 :(得分:2)

对于遇到此问题的任何人,如果您对整个对象进行存根或监视,然后再做

sandbox.restore()

您仍然会收到错误消息。您必须存根/监视各个方法。

我永远都在试图找出问题所在。

sinon-7.5.0

答案 5 :(得分:1)

此错误是由于未正确恢复存根功能引起的。使用沙箱,然后使用沙箱创建存根。在测试套件恢复沙箱之后。

  before(() => {
      sandbox         = sinon.sandbox.create();
      mockObj         = sandbox.stub(testApp, 'getObj', fake_function)
  });
  after(() => {
      sandbox.restore();
  });

答案 6 :(得分:0)

即使使用沙箱,也可能会出现错误。尤其是当针对ES6类并行运行测试时。

const sb = sandbox.create();

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

如果另一个测试试图从原型中提取myFunc,这可能会引发相同的错误。 我能够解决这个问题,但我对此不感到骄傲...

const sb = sandbox.create();

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

答案 7 :(得分:0)

我遇到了间谍。这种行为使sinon非常不灵活。我创建了一个辅助函数,该函数尝试在设置新间谍之前删除所有现有的间谍。这样,我不必担心任何之前/之后的状态。类似的方法也可能适用于存根。

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

答案 8 :(得分:0)

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

,并在测试中创建存根时使用此功能。它将解决“ Sinon错误,试图包装已包装的功能”错误。

示例:

stub(Validator.prototype, 'canGeneratePayment').returns(Promise.resolve({ indent: dTruckIndent }));

答案 9 :(得分:0)

只是提醒一下,因为我花了大约一个小时才弄明白:

如果您有两个(或更多)测试文件,并且无论您尝试什么方法都仍然遇到“已包装”错误,请确保您的 beforeEach 和 {{1} } 存根/替换处理程序位于测试文件的 afterEach 块内。

如果您将它们放在全局测试范围内,即在 describe 结构之外,sinon 将尝试两次并抛出这个。