Erro de Sinon Tentativa de envolver a função que já está embrulhada

94

Embora haja uma mesma pergunta aqui, mas eu não consegui encontrar uma resposta para o meu problema, então aqui vai minha pergunta:

Estou testando meu aplicativo node js usando mocha e chai. Estou usando o sinion para envolver minha função.

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

Quando tento executar este teste, ocorre um erro

Attempted to wrap getObj which is already wrapped

Eu também tentei colocar

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

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

em cada descrição, mas ainda me dando o mesmo erro.

Rohit Vyavahare
fonte
Você pode encontrar uma explicação na parte inferior do post aqui
Nir Alfasi

Respostas:

114

Você deve restaurar a função getObjin after(), tente como abaixo.

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();
    });
});
zangw
fonte
Depois de tentar a forma aceita acima, estou recebendo o mesmo erro no gancho "antes de tudo"
Ashwin Hegde
@AshwinHegde, você poderia me dar seus códigos de teste? Talvez eu possa encontrar algum problema aqui.
zangw
1
Não há como restaurar todos os stubs sem especificar cada um? Seria ótimo ter um sinon.restoreAll();que pudesse ser executado após todos os testes, apenas para garantir que você não se esqueça de restaurar um stub.
Lucas
afterEach (() => {sinon.verifyAndRestore ();});
Sam T
21

Este erro é devido à não restauração da função stub corretamente. Use a caixa de areia e, a seguir, crie o esboço usando a caixa de areia. Após cada teste dentro do pacote, restaure o sandbox

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

  afterEach(() => {
      sandbox.restore();
  });
Arjun Malik
fonte
1
cara, salvou minha vida)
Yegor Zaremba
Isso funcionou para mim. Eu sinto que essa deve ser a resposta aceita.
Daniel Kaplan
Tive vários testes com funções de empacotamento e preciso usar afterEach .
Richard
No meu caso, essa foi a resposta correta, pois eu estava espionando um objeto inteiro e não um método específico, então não pude restaurar.
Edison Spencer
11

Para os casos em que você precisa restaurar todos os métodos de um objeto, você pode usar o sinon.restore(obj).

Exemplo:

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

after(() => {
    sinon.restore(userRepository);
});
Renan Ferreira
fonte
1
Isso não funcionou para mim ao criar stub para funções no objeto. Tive que restaurar por função, como mostra a resposta aceita.
Ian Robertson
7
sinon.restore () tornou-se obsoleto no Sinon v2 e foi removido posteriormente. // Previously sinon.restore(stubObject); // Typescript (stubObject as any).restore(); // Javascript stubObject.restore();
MatthiasSommer
6

Eu também estava acertando isso usando os ganchos antes () e depois () do Mocha. Eu também estava usando restore () conforme mencionado em todos os lugares. Um único arquivo de teste funcionou bem, vários não. Finalmente descobri sobre ganchos de nível de raiz Mocha : Eu não tinha meu before () e after () dentro do meu describe (). Portanto, ele encontra todos os arquivos com before () no nível da raiz e os executa antes de iniciar qualquer teste.

Portanto, certifique-se de ter um padrão semelhante:

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
fonte
3

É aconselhável inicializar os stubs em 'beforeEach' e restaurá-los em 'afterEach'. Mas caso você esteja se sentindo aventureiro, o seguinte também funciona.

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
fonte
3

Mesmo com o sandbox, ele pode dar o erro. Especialmente quando os testes são executados em paralelo para as classes ES6.

const sb = sandbox.create();

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

isso pode gerar o mesmo erro se outro teste estiver tentando stub myFunc do Prototype. Consegui consertar isso, mas não tenho orgulho disso ...

const sb = sandbox.create();

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

Para qualquer pessoa com esse problema, se você fizer stub ou espionar o objeto inteiro, e mais tarde fizer

sandbox.restore ()

Você ainda receberá o erro. Você tem que stub / espiar os métodos individuais.

Eu perdi uma eternidade tentando descobrir o que estava errado.

sinon-7.5.0

Khon Lieu
fonte
2

Eu corri para isso com espiões. Esse comportamento torna o sinon bastante inflexível para trabalhar. Eu criei uma função auxiliar que tenta remover qualquer espião existente antes de definir um novo. Dessa forma, não preciso me preocupar com nenhum estado de antes / depois. Uma abordagem semelhante também pode funcionar para stubs.

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

e use esta função ao criar stubs em testes. Ele resolverá o erro 'Erro de Sinon Tentativa de função de agrupamento que já está agrupado'.

exemplo:

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