Błąd Sinon Próbowano opakować funkcję, która jest już opakowana

91

Chociaż jest tutaj to samo pytanie, ale nie mogłem znaleźć odpowiedzi na mój problem, oto moje pytanie:

Testuję moją aplikację node js przy użyciu mocha i chai. Używam sinion do zawijania mojej funkcji.

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

Kiedy próbuję uruchomić ten test, wyświetla mi się błąd

Attempted to wrap getObj which is already wrapped

Próbowałem też umieścić

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

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

w każdym opisie, ale nadal daje mi ten sam błąd.

Rohit Vyavahare
źródło
Możesz znaleźć wyjaśnienie na dole postu tutaj
Nir Alfasi

Odpowiedzi:

113

Powinieneś przywrócić funkcję getObjin after(), wypróbuj ją jak poniżej.

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
źródło
Po wypróbowaniu powyższego, akceptowanego sposobu, otrzymuję ten sam błąd pod hakiem „before all”
Ashwin Hegde
@AshwinHegde, czy możesz podać mi swoje kody testowe? Może znajdę tu jakiś problem.
zangw
1
Czy nie ma możliwości przywrócenia wszystkich kodów pośredniczących bez określenia każdego z nich? Byłoby wspaniale mieć program, sinon.restoreAll();który można uruchomić po wszystkich testach, aby upewnić się, że nie zapomnisz przywrócić kodu pośredniczącego.
Łukasz
afterEach (() => {sinon.verifyAndRestore ();});
Sam T
20

Ten błąd jest spowodowany nieprawidłowym przywróceniem funkcji kodu pośredniczącego. Użyj piaskownicy, a następnie utwórz kod pośredniczący za pomocą piaskownicy. Po każdym teście w pakiecie przywróć piaskownicę

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

  afterEach(() => {
      sandbox.restore();
  });
Arjun Malik
źródło
1
koleś, uratował mi życie)
Jegor Zaremba
To zadziałało dla mnie. Czuję, że to powinna być akceptowana odpowiedź.
Daniel Kaplan,
Miałem wiele testów z funkcjami opakowującymi i muszę użyć afterEach .
Richard
W moim przypadku była to poprawna odpowiedź, ponieważ szpiegowałem cały obiekt, a nie konkretną metodę, więc nie mogłem przywrócić.
Edison Spencer
11

W przypadkach, w których musisz przywrócić wszystkie metody jednego obiektu, możesz użyć sinon.restore(obj).

Przykład:

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

after(() => {
    sinon.restore(userRepository);
});
Renan Ferreira
źródło
1
To nie zadziałało, gdy funkcja stubbing na obiekcie. Musiałem przywrócić każdą funkcję, tak jak pokazuje zaakceptowana odpowiedź.
Ian Robertson
7
sinon.restore () został uznany za przestarzały w Sinon v2, a następnie usunięty. // Previously sinon.restore(stubObject); // Typescript (stubObject as any).restore(); // Javascript stubObject.restore();
MatthiasSommer
6

Uderzałem w to również za pomocą haków przed () i after () w Mocha. Używałem również funkcji restore (), jak wspomniano wszędzie. Pojedynczy plik testowy działał poprawnie, wiele nie. Wreszcie znalazłem informacje o hakach poziomu głównego Mocha : nie miałem moich przed () i after () w moim własnym opisie (). Znajduje więc wszystkie pliki z przed () na poziomie głównym i wykonuje je przed rozpoczęciem jakichkolwiek testów.

Więc upewnij się, że masz podobny wzór:

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
źródło
3

Zaleca się zainicjowanie kodów pośredniczących w „beforeEach” i przywrócenie ich w „afterEach”. Ale jeśli masz ochotę na przygodę, działa również poniższe rozwiązanie.

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
źródło
3

Nawet z piaskownicą może to spowodować błąd. Zwłaszcza, gdy testy są wykonywane równolegle dla klas ES6.

const sb = sandbox.create();

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

może to spowodować ten sam błąd, jeśli inny test próbuje odgiąć myFunc z prototypu. Udało mi się to naprawić, ale nie jestem z tego dumny ...

const sb = sandbox.create();

before(() => {
  MyObj.prototype.myFunc = sb.stub().callsFake(() => {
    return 'whatever';
  });
});
after(() => {
  sb.restore();
});
Tonino
źródło
3

Dla każdego, kto napotka ten problem, jeśli zablokujesz lub szpiegujesz cały obiekt, a później to zrobisz

sandbox.restore ()

Nadal będziesz otrzymywać błąd. Musisz zablokować / szpiegować poszczególne metody.

Straciłem wieczność próbując dowiedzieć się, co było nie tak.

sinon-7.5.0

Khon Lieu
źródło
2

Wpadłem na to ze szpiegami. Takie zachowanie sprawia, że ​​praca z sinonem jest dość nieelastyczna. Stworzyłem funkcję pomocniczą, która próbuje usunąć istniejącego szpiega przed ustawieniem nowego. W ten sposób nie muszę się martwić o stan przed / po. Podobne podejście może działać również w przypadku kodów pośredniczących.

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
źródło
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);
    }

i użyj tej funkcji podczas tworzenia kodów pośredniczących w testach. To rozwiąże błąd „Sinon error Próbowano opakować funkcję, która jest już opakowana”.

przykład:

stub(Validator.prototype, 'canGeneratePayment').returns(Promise.resolve({ indent: dTruckIndent }));
gramcha
źródło