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


91

虽然这里有一个相同的问题,但是我找不到我的问题的答案,所以这里是我的问题:

我正在使用mocha和chai测试我的node js应用程序。我正在用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 getObj which is already wrapped

我也尝试过

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

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

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


您可以在文章底部的解释这里
尼尔Alfasi

Answers:


113

您应该恢复getObjinafter()功能,请按以下方法尝试。

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

在尝试了上述公认的方式之后,我在“之前”钩子下得到了相同的错误
Ashwin Hegde

@AshwinHegde,能给我您的测试代码吗?也许我可以在这里找到一些问题。
zangw

1
没有指定每个存根,是否无法还原所有存根?拥有一个sinon.restoreAll();可以在所有测试之后都可以运行的代码,这很不错,只是为了确保您不会忘记恢复存根。
路加福音

afterEach(()=> {sinon.verifyAndRestore();});
山姆T

20

此错误是由于未正确恢复存根功能。使用沙箱,然后使用沙箱创建存根。在套件中进行每次测试后,请还原沙箱

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

  afterEach(() => {
      sandbox.restore();
  });

1
花花公子,救了我的命)
Yegor Zaremba

这对我有用。我觉得这应该是公认的答案。
丹尼尔·卡普兰

我使用包装功能进行了多次测试,需要使用afterEach
理查德·

就我而言,这是正确的答案,因为我监视的是整个对象而不是特定的方法,所以无法恢复。
爱迪生斯宾塞

11

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

例:

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

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

1
在对象上添加函数时,这对我不起作用。我必须按照接受的答案显示的那样恢复每个功能。
伊恩·罗伯逊

7
Sinon v2中不推荐使用sinon.restore(),之后将其删除。 // Previously sinon.restore(stubObject); // Typescript (stubObject as any).restore(); // Javascript stubObject.restore();
MatthiasSommer

6

我也使用Mocha的before()和after()钩子来实现这一点。我也在使用到处都提到的restore()。单个测试文件运行良好,多个没有。终于发现了Mocha的根级挂钩:我自己的describe()中没有我的before()和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

建议在“ 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();
  });
}

3

即使使用沙箱,它也会给您错误。尤其是针对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();
});

3

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

sandbox.restore()

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

我永远浪费时间试图找出问题所在。

sinon-7.5.0


2

我遇到间谍了。这种行为使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);
};


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 }));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.