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

这有两个原因,为什么这行得通,而原始问题中的测试却没有:

  • 我创建了两个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 ,我提出了三种使用模拟进行呼叫计数验证的替代方法:

  • 允许连接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`?
  • 使用能记住调用次数的 Spy API。

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

这有两个原因,为什么这行得通,而原始问题中的测试却没有:

  • 我创建了两个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 ,我提出了三种使用模拟进行呼叫计数验证的替代方法:

  • 允许连接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`?
  • 使用能记住调用次数的 Spy API。

此问题已自动标记为过时,因为它最近没有活动。 如果没有进一步的活动发生,它将被关闭。 感谢你的贡献。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

akdor1154 picture akdor1154  ·  4评论

NathanHazout picture NathanHazout  ·  3评论

kevinburkeshyp picture kevinburkeshyp  ·  4评论

tinganho picture tinganho  ·  3评论

pavelkornev picture pavelkornev  ·  4评论