Sinon: withArgs لا يعمل بشكل صحيح مع sinon.mock

تم إنشاؤها على ٣٠ أغسطس ٢٠١٧  ·  3تعليقات  ·  مصدر: sinonjs/sinon

  • إصدار Sinon: 3.2.1
  • البيئة: OSX ، العقدة 7
  • المكتبات الأخرى التي تستخدمها: Mocha ، mjackson / توقع

ماذا تتوقع أن يحدث؟
عندما أقوم بإنشاء محاكاة للفصل ومحاولة التحكم في سلوك إحدى وظائفه باستخدام withArgs ، يتم طرح استثناء. يحدث فقط عند استخدام مكالمات متعددة withArgs .

ما يحدث بالفعل

كيف تتكاثر


مثال قابل للتكرار

افترض أنني أعلنت عن فئة C :

      class C {
        static foo(arg1, arg2) {return 'foo';}
      }

أحاول التحكم في سلوك foo باستخدام محاكاة على C :

      it('test withArgs mock', () => {
        let CMock = sinon.mock(C);
        let fooStub = CMock.expects('foo');

        fooStub.withArgs('a', 'b').returns(1);
        fooStub.withArgs('c', 'd').returns(2);

        expect(fooStub('a', 'b')).toEqual(1);
        expect(fooStub('c', 'd')).toEqual(2);

        CMock.restore();
      });

مما يطرح الاستثناء التالي:

خطأ توقع: تلقي foo وسيطات خاطئة ["أ" ، "ب"] ، متوقع ["ج" ، "د"]
في Object.fail (node_modules / sinon / lib / sinon / mock-المتوقعة.js: 281: 25)
في الوظيفة.(node_modules / sinon / lib / sinon / mock-المتوقعة.js: 182: 33)
في Array.forEach (أصلي)
في Function.verifyCallAllowed (node_modules / sinon / lib / sinon / mock-المتوقعة.js: 175: 27)
في Function.invoke (node_modules / sinon / lib / sinon / mock-المتوقعة.js: 78: 14)
في الوكيل (node_modules / sinon / lib / sinon / spy.js: 89: 22)

أثناء عمل هذا الرمز:

      it('test withArgs stub', () => {
        let fooStub = sinon.stub(C, 'foo');

        fooStub.withArgs('a', 'b').returns(1);
        fooStub.withArgs('c', 'd').returns(2);

        expect(fooStub('a', 'b')).toEqual(1);
        expect(fooStub('c', 'd')).toEqual(2);

        fooStub.restore();
      });

الجدير بالذكر أن السيناريوهات التالية تعمل بالشكل المتوقع:

      it('test one withArgs mock', () => {
        let CMock = sinon.mock(C);
        let fooStub = CMock.expects('foo');

        fooStub.withArgs('a', 'b').returns(1);

        expect(fooStub('a', 'b')).toEqual(1); // ok

        CMock.restore();
      });
      it('test one withArgs mock', () => {
        let CMock = sinon.mock(C);
        let fooStub = CMock.expects('foo');

        fooStub.withArgs('a', 'b').returns(1);

        expect(fooStub('c', 'd')).toEqual(1); // error: foo received wrong arguments ["c", "d"], expected ["a", "b"]

        CMock.restore();
      });

بمعنى ، تحدث المشكلة فقط عند استخدام withArgs عدة مرات.
قد يكون مرتبطًا بـ # 1381 ، ولكن كما ترى في المثال ، فإن الحل المقترح هناك لا يعمل.

Bug Medium Help wanted stale

التعليق الأكثر فائدة

ألقيت نظرة على الكود.
بادئ ذي بدء ، يعمل هذا الاختبار:

class C {
  static foo(arg1, arg2) {return 'foo';}
}

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(C.foo('a', 'b')).toEqual(1);
  expect(C.foo('c', 'd')).toEqual(2);

  CMock.restore();
});

هناك سببان يبرران نجاح هذا الاختبار ولا الاختبار في الإصدار الأصلي:

  • أنشأت اثنين expects .
  • أسمي الطريقة الثابتة بدلاً من الطريقة التي يتم إرجاعها بواسطة expects .

بمعنى ، هذا لا يعمل أيضًا:

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  let fooStub = CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(fooStub('a', 'b')).toEqual(1); // Error: foo received wrong arguments ["a", "b"], expected ["c", "d"]
  expect(fooStub('c', 'd')).toEqual(2);

  CMock.restore();
});

لماذا يحدث ذلك؟

في كل مرة يتم استدعاء expects ، يحدث ما يلي:

  • يتم إنشاء توقع جديد (كعب محسّن بـ minCalls/maxCalls - وهي المتغيرات التي يتم تغييرها عند استدعاء once() على سبيل المثال) ، فقط أنه "كعب محسّن" ، لا لديك اتصال بغيره من expects . إنه العنصر الذي تم إرجاعه من expects .
  • يضاف إلى قائمة التوقعات.

مما ينتج عنه سلوكيات مختلفة لهذه السيناريوهات:

  • إذا اتصلت بالطريقة التي تم إرجاعها بواسطة expects ، فستقتصر فقط على expects .
  • إذا اتصلت بالطريقة مباشرة وليس كعب الروتين ، فستبحث في جميع expects وتجد الطريقة المناسبة. إذا كان هناك أكثر من واحد ، فسوف يستدعي الأول.

هل هذا هو السلوك الصحيح؟ يبدو لي أكثر بديهية أنه بمجرد اتصال شخص ما بـ expects على طريقة ما ، سوف يتصرف تمامًا مثل stub (مع minCalls/maxCalls ). سينتج عن ذلك:

  • يمكن مشاركة المزيد من الأكواد بين stub و mock-expectation .
  • سيكون سلوك السيناريوهات أعلاه هو نفسه.

الآن ، ماذا سيحدث لـ minCalls/maxCalls ؟

نظرًا لوجود expectation واحد فقط الآن ، أقترح ثلاث طرق بديلة لإجراء التحقق من عدد المكالمات باستخدام mocks:

  • السماح لـ concat once() ، twice() إلخ.
  let fooStub = CMock.expects('foo').withArgs('a', 'b').returns(1).twice();
  fooStub.withArgs('c', 'd').returns(2).once(); // Does not affect the previous expectation.

قد يكون محيرا بعض الشيء بالرغم من ذلك.

  • لا تسمح لـ concat once() ، twice() إلخ.
  CMock.expects('foo').withArgs('a', 'b').returns(1).twice(); // `twice()` returns `null`, so that one has to call `expects()` again to set expectations for other arguments. 
  CMock.expects('foo').withArgs('c', 'd').returns(2).once();

أكثر وضوحًا ، لكن لا يمكنك إيقاف foo أكثر من مرة ، لذا سنحتاج إلى طريقة لتجاوز ذلك. أيضًا ، لا يزال هذا ممكنًا:

  CMock.expects('foo').withArgs('a', 'b').returns(1).withArgs('c', 'd').returns(2).twice(); // I guess that this should only affect the last `withArgs`?
  • استخدم Spy API الذي يتذكر عدد المكالمات.

ال 3 كومينتر

شكرا لتقرير المشكلة النموذجي . لا أستخدم وظيفة mocks ، لأنها تجعل عقلي يؤلمني ، لذلك سيحتاج شخص آخر إلى إلقاء نظرة على هذا ، ولكن أمثلةك ستجعل ذلك أسهل كثيرًا.

لا تتردد في إلقاء نظرة على الكود بنفسك - غالبًا ما تكون أسرع طريقة لإصلاح هذا :-)

ألقيت نظرة على الكود.
بادئ ذي بدء ، يعمل هذا الاختبار:

class C {
  static foo(arg1, arg2) {return 'foo';}
}

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(C.foo('a', 'b')).toEqual(1);
  expect(C.foo('c', 'd')).toEqual(2);

  CMock.restore();
});

هناك سببان يبرران نجاح هذا الاختبار ولا الاختبار في الإصدار الأصلي:

  • أنشأت اثنين expects .
  • أسمي الطريقة الثابتة بدلاً من الطريقة التي يتم إرجاعها بواسطة expects .

بمعنى ، هذا لا يعمل أيضًا:

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  let fooStub = CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(fooStub('a', 'b')).toEqual(1); // Error: foo received wrong arguments ["a", "b"], expected ["c", "d"]
  expect(fooStub('c', 'd')).toEqual(2);

  CMock.restore();
});

لماذا يحدث ذلك؟

في كل مرة يتم استدعاء expects ، يحدث ما يلي:

  • يتم إنشاء توقع جديد (كعب محسّن بـ minCalls/maxCalls - وهي المتغيرات التي يتم تغييرها عند استدعاء once() على سبيل المثال) ، فقط أنه "كعب محسّن" ، لا لديك اتصال بغيره من expects . إنه العنصر الذي تم إرجاعه من expects .
  • يضاف إلى قائمة التوقعات.

مما ينتج عنه سلوكيات مختلفة لهذه السيناريوهات:

  • إذا اتصلت بالطريقة التي تم إرجاعها بواسطة expects ، فستقتصر فقط على expects .
  • إذا اتصلت بالطريقة مباشرة وليس كعب الروتين ، فستبحث في جميع expects وتجد الطريقة المناسبة. إذا كان هناك أكثر من واحد ، فسوف يستدعي الأول.

هل هذا هو السلوك الصحيح؟ يبدو لي أكثر بديهية أنه بمجرد اتصال شخص ما بـ expects على طريقة ما ، سوف يتصرف تمامًا مثل stub (مع minCalls/maxCalls ). سينتج عن ذلك:

  • يمكن مشاركة المزيد من الأكواد بين stub و mock-expectation .
  • سيكون سلوك السيناريوهات أعلاه هو نفسه.

الآن ، ماذا سيحدث لـ minCalls/maxCalls ؟

نظرًا لوجود expectation واحد فقط الآن ، أقترح ثلاث طرق بديلة لإجراء التحقق من عدد المكالمات باستخدام mocks:

  • السماح لـ concat once() ، twice() إلخ.
  let fooStub = CMock.expects('foo').withArgs('a', 'b').returns(1).twice();
  fooStub.withArgs('c', 'd').returns(2).once(); // Does not affect the previous expectation.

قد يكون محيرا بعض الشيء بالرغم من ذلك.

  • لا تسمح لـ concat once() ، twice() إلخ.
  CMock.expects('foo').withArgs('a', 'b').returns(1).twice(); // `twice()` returns `null`, so that one has to call `expects()` again to set expectations for other arguments. 
  CMock.expects('foo').withArgs('c', 'd').returns(2).once();

أكثر وضوحًا ، لكن لا يمكنك إيقاف foo أكثر من مرة ، لذا سنحتاج إلى طريقة لتجاوز ذلك. أيضًا ، لا يزال هذا ممكنًا:

  CMock.expects('foo').withArgs('a', 'b').returns(1).withArgs('c', 'd').returns(2).twice(); // I guess that this should only affect the last `withArgs`?
  • استخدم Spy API الذي يتذكر عدد المكالمات.

تم وضع علامة على هذه المشكلة تلقائيًا على أنها قديمة نظرًا لعدم وجود نشاط حديث لها. سيتم إغلاقه إذا لم يحدث أي نشاط آخر. شكرا لمساهماتكم.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

fearphage picture fearphage  ·  4تعليقات

JakobJingleheimer picture JakobJingleheimer  ·  3تعليقات

stephanwlee picture stephanwlee  ·  3تعليقات

stevenmusumeche picture stevenmusumeche  ·  3تعليقات

fearphage picture fearphage  ·  3تعليقات