正如我在评论中提到的,Mockito 实际上是以不直观的方式有状态的;很多时候,存根或验证的方法只是“最后调用的方法”,主要是因为像verify(foo).doA() 这样的语法实际上调用了doA,而不是将方法doA 的反射引用传递给Mockito。这与在存根或验证过程中调用相同模拟的语法完全不兼容。
我有written about this before with regard to Matchers,在存根期间也有同样的问题。查看源代码,您可以看到验证存在同样的问题,至少在同一个 mock 上调用方法时是这样。
简而言之,验证实际上是一个三阶段的过程:
- 致电
verify(mockedThing)。
- 如有必要,按顺序调用匹配器。不要在
mockedThing 上调用任何方法。
- 在
mockedThing 上调用您正在验证的方法,如果您不使用匹配器,则使用实际参数值,如果您使用匹配器,则使用虚拟(忽略)参数值。由于 Mockito 在后台跟踪匹配器堆栈,因此匹配器方法可以返回 0 或 null,而 Mockito 不会认为这些是要检查的值。
幕后
调用verify实际上只是set a flag and return the exact same mock:
public <T> T verify(T mock, VerificationMode mode) {
// [catch errors]
mockingProgress.verificationStarted(new MockAwareVerificationMode(mock, mode));
return mock;
}
然后,inside the handler that handles all mock invocations,Mockito 在验证开始后第一次调用模拟时开始验证:
public Object handle(Invocation invocation) throws Throwable {
// [detect doAnswer stubbing]
VerificationMode verificationMode = mockingProgress.pullVerificationMode();
// [check Matcher state]
// if verificationMode is not null then someone is doing verify()
if (verificationMode != null) {
// We need to check if verification was started on the correct mock
// - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138)
if (((MockAwareVerificationMode) verificationMode).getMock() == invocation.getMock()) {
VerificationDataImpl data = createVerificationData(invocationContainerImpl, invocationMatcher);
verificationMode.verify(data);
return null;
} else {
// this means there is an invocation on a different mock. Re-adding verification mode
// - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138)
mockingProgress.verificationStarted(verificationMode);
}
}
// [prepare invocation for stubbing]
}
因此,如果您与 mock 进行交互只是为了获取参数值,Mockito 将假定您实际上是在调用该方法进行验证。请注意,如果调用稍有不同,例如 verify(mockedThing).doB(eq(5), eq(mockedThing.doA())); 带有额外的 eq(5),您将收到有关滥用匹配器的不同错误消息 - 特别是因为 Mockito 不只是认为您正在验证 doA,而是你以某种方式认为doA 需要争论。
后果
您的代码不起作用:
// DOESN'T WORK
verify(mockedThing).doB(eq(mockedThing.doA()));
// BECAUSE IT BEHAVES THE SAME AS
verify(mockedThing).doA();
但提取它确实有效:
// WORKS, though it makes an extra call to doA
Value value = mockedThing.doA();
verify(mockedThing).doB(eq(value));
这也有效,并且展示了幕后发生的事情,但永远不要在真正的测试中写这个:
// WORKS BUT DON'T EVER ACTUALLY DO THIS
Value value = mockedThing.doA();
verify(mockedThing);
eq(value);
mockedThing.doB(8675309 /* dummy value ignored because of matcher */);