ข้อผิดพลาด Sinon พยายามตัดฟังก์ชันที่ถูกรวมไว้แล้ว


92

แม้ว่าจะมีคำถามเดียวกันที่นี่ แต่ฉันไม่สามารถหาคำตอบสำหรับปัญหาของฉันได้ดังนั้นคำถามของฉันจึงไปที่นี่:

ฉันกำลังทดสอบแอพ 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();
});

ในแต่ละคำอธิบาย แต่ยังคงให้ข้อผิดพลาดเดียวกันกับฉัน


คุณสามารถดูคำอธิบายได้ที่ด้านล่างของโพสต์ที่นี่
Nir Alfasi

คำตอบ:


113

คุณควรเรียกคืนฟังก์ชันgetObjin 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();
    });
});

หลังจากลองใช้วิธีที่ยอมรับข้างต้นฉันได้รับข้อผิดพลาดเดียวกันภายใต้ hook "ก่อนทั้งหมด"
Ashwin Hegde

@AshwinHegde คุณช่วยกรุณาให้รหัสทดสอบของคุณได้ไหม บางทีฉันอาจพบปัญหาบางอย่างที่นี่
zangw

1
ไม่มีวิธีใดในการกู้คืนต้นขั้วทั้งหมดโดยไม่ระบุแต่ละอัน? จะเป็นการดีที่จะมีสิ่งsinon.restoreAll();ที่สามารถรันได้หลังจากการทดสอบทั้งหมดเพียงเพื่อให้แน่ใจว่าคุณไม่ลืมที่จะกู้คืนต้นขั้ว
Luke

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

20

ข้อผิดพลาดนี้เกิดจากการไม่คืนค่าฟังก์ชัน Stub อย่างถูกต้อง ใช้แซนด์บ็อกซ์แล้วสร้างต้นขั้วโดยใช้แซนด์บ็อกซ์ หลังจากการทดสอบแต่ละครั้งภายในชุดให้คืนค่าแซนด์บ็อกซ์

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

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

1
เพื่อนช่วยชีวิตฉัน)
Yegor Zaremba

สิ่งนี้ได้ผลสำหรับฉัน ฉันรู้สึกว่านี่ควรเป็นคำตอบที่ได้รับการยอมรับ
Daniel Kaplan

ฉันมีการทดสอบหลายกับฟังก์ชั่นการตัดและความจำเป็นที่จะใช้afterEach
Richard

ในกรณีของฉันนี่เป็นคำตอบที่ถูกต้องเนื่องจากฉันกำลังสอดแนมวัตถุทั้งหมดไม่ใช่วิธีการเฉพาะดังนั้นฉันจึงไม่สามารถกู้คืนได้
Edison Spencer

11

สำหรับกรณีที่คุณต้องการคืนค่าวิธีการทั้งหมดของออบเจ็กต์เดียวคุณสามารถใช้ไฟล์sinon.restore(obj).

ตัวอย่าง:

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

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

1
สิ่งนี้ใช้ไม่ได้ผลสำหรับฉันเมื่อทำหน้าที่สะดุดกับวัตถุ ฉันต้องเรียกคืนต่อฟังก์ชันเหมือนที่คำตอบที่ยอมรับแสดง
Ian Robertson

7
sinon.restore () เลิกใช้งานแล้วใน Sinon v2 และถูกลบออกในภายหลัง // Previously sinon.restore(stubObject); // Typescript (stubObject as any).restore(); // Javascript stubObject.restore();
MatthiasSommer

6

ฉันยังกดปุ่มนี้โดยใช้ตะขอ before () และหลัง () ของ Mocha ฉันยังใช้การคืนค่า () ตามที่กล่าวไว้ทุกที่ ไฟล์ทดสอบเดียวทำงานได้ดีหลายไฟล์ไม่ได้ ในที่สุดก็พบเกี่ยวกับMocha root-level-hooks : ฉันไม่มี 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 จาก Prototype ฉันสามารถแก้ไขได้ แต่ฉันไม่ภูมิใจกับมัน ...

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 error พยายามที่จะตัดฟังก์ชันที่ถูกรวมไว้แล้ว'

ตัวอย่าง:

stub(Validator.prototype, 'canGeneratePayment').returns(Promise.resolve({ indent: dTruckIndent }));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.