Sinon: withArgsはsinon.mockでは正しく機能しません

作成日 2017年08月30日  ·  3コメント  ·  ソース: sinonjs/sinon

  • シノンバージョン:3.2.1
  • 環境:OSX、ノード7
  • 使用している他のライブラリ:Mocha、mjackson / expect

何が起こると思いましたか?
クラスにモックを作成し、 withArgsを使用してその関数のいずれかの動作を制御しようとすると、例外がスローされます。 withArgs複数の呼び出しを使用する場合にのみ発生します。

実際に何が起こるか

再現する方法


再現可能な例

クラスCを宣言したと仮定します。

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

Cモックを使用してfooの動作を制御しようとしています:

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

これは次の例外をスローします:

ExpectationError:fooが間違った引数を受け取りました["a"、 "b"]、予期された["c"、 "d"]
Object.failで(node_modules / sinon / lib / sinon / mock-expectation.js:281:25)
関数で。(node_modules / sinon / lib / sinon / mock-expectation.js:182:33)
Array.forEach(ネイティブ)で
Function.verifyCallAllowed(node_modules / sinon / lib / sinon / mock-expectation.js:175:27)で
Function.invokeで(node_modules / sinon / lib / sinon / mock-expectation.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();
});

これが機能する理由と元の問題のテストが機能しない理由は2つあります。

  • 2つの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 )というのがより直感的に思えます。 これにより、次のようになります。

  • stubmock-expectation間でより多くのコードを共有できます。
  • 上記のシナリオの動作は同じになります。

さて、 minCalls/maxCallsどうなりますか?

現在expectationは1つしかないため、モックを使用して呼び出し数の検証を行う3つの代替方法を提案します。

  • 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.

ただし、少し混乱するかもしれません。

  • 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`?
  • 呼び出し回数を記憶するSpyAPIを使用します。

全てのコメント3件

例示的な問題レポートをありがとう。 私はモック機能を使用していません。脳が痛くなるので、他の誰かがこれを見る必要がありますが、あなたの例はそれをはるかに簡単にするはずです。

自分でコードを自由に見てください-多くの場合、これを修正する最も簡単な方法です:-)

コードを見てみました。
まず第一に、このテストは機能します:

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

これが機能する理由と元の問題のテストが機能しない理由は2つあります。

  • 2つの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 )というのがより直感的に思えます。 これにより、次のようになります。

  • stubmock-expectation間でより多くのコードを共有できます。
  • 上記のシナリオの動作は同じになります。

さて、 minCalls/maxCallsどうなりますか?

現在expectationは1つしかないため、モックを使用して呼び出し数の検証を行う3つの代替方法を提案します。

  • 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.

ただし、少し混乱するかもしれません。

  • 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`?
  • 呼び出し回数を記憶するSpyAPIを使用します。

この問題は、最近のアクティビティがないため、自動的に古いものとしてマークされています。 それ以上のアクティビティが発生しない場合は閉じられます。 貢献していただきありがとうございます。

このページは役に立ちましたか?
0 / 5 - 0 評価