你期望会发生什么?
当我在类上创建模拟并尝试使用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 相关,但正如您在示例中所看到的,建议的解决方案在那里不起作用。
感谢您提供示例问题报告。 我不使用模拟功能,因为它让我的大脑受伤,所以其他人需要看看这个,但你的例子应该会让这更容易。
随意看看自己的代码 - 通常是解决这个问题的最快方法:-)
我看了一下代码。
首先,这个测试有效:
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
,我提出了三种使用模拟进行呼叫计数验证的替代方法:
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`?
此问题已自动标记为过时,因为它最近没有活动。 如果没有进一步的活动发生,它将被关闭。 感谢你的贡献。
最有用的评论
我看了一下代码。
首先,这个测试有效:
这有两个原因,为什么这行得通,而原始问题中的测试却没有:
expects
。expects
返回的方法。意思是,这也不起作用:
为什么会发生?
每次调用
expects
,都会发生以下情况:minCalls/maxCalls
增强的存根 - 例如,当您调用once()
时会更改的变量)被创建,只是它是一个“范围存根”,它没有与其他expects
。 它是从expects
返回的对象。对于这些场景,这会导致不同的行为:
expects
返回的方法,它将仅限于expects
。expects
并找到一个适合的。 如果有多个,它将调用第一个。这是正确的行为吗? 在我看来更直观的是,一旦有人在方法上调用
expects
,它的行为就会与stub
完全一样(带有额外的minCalls/maxCalls
)。 这将导致:stub
和mock-expectation
之间共享。现在,
minCalls/maxCalls
会发生什么?由于现在只有一个
expectation
,我提出了三种使用模拟进行呼叫计数验证的替代方法:once()
、twice()
等。不过可能有点混乱。
once()
、twice()
等。更清楚,但您不能多次存根
foo
,因此我们需要一种绕过它的方法。 此外,这仍然是可能的: