无法在verify的参数中使用模拟函数调用:调用过多



设置如下:

//call doA a bunch of times, call doB once using some value that depends on doA()
verify(mockedThing).doB(eq(mockedThing.doA())); //removing eq() changes nothing

显然,doA()被配置为返回一些值,而mockedThing确实被嘲笑了。结果是:mockito抱怨我调用doA(此处强调:NOT doB!)太频繁了,而且它希望只调用一次!

以下更改有效:

int result = mockedThing.doA() 
verify(mockedThing).doB(eq(result));

我的问题很简单:这里发生了什么?为什么Mockito要验证我传递给函数的参数的调用,而不是函数本身的调用?

正如我在评论中提到的,Mockito实际上是以非直觉的方式有状态的;很多时候,被存根或验证的方法只是"最后调用的方法",主要是因为像verify(foo).doA()这样的语法实际上调用doA,而不是将对方法doA的反射引用传递到Mockito中。这与在存根或验证过程中调用相同mock的语法根本不兼容。

我以前写过关于Matchers的文章,它们在存根处理过程中也有同样的问题。翻阅源代码,您可以看到验证方面的相同问题,至少在调用同一mock上的方法时是这样。

简而言之,验证实际上是一个三个阶段的过程:

  1. 致电verify(mockedThing)
  2. 如有必要,请按顺序呼叫匹配者。不要在mockedThing上调用任何方法
  3. 调用在mockedThing上验证的方法,如果不使用匹配器,则使用实际参数值,如果使用匹配器则使用伪(忽略)参数值。由于Mockito在后台跟踪匹配器堆栈,因此匹配器方法可以返回0null,而Mockito不会认为这些是要检查的值

在掩护下

verify的调用实际上只是设置了一个标志并返回完全相同的mock:

public <T> T verify(T mock, VerificationMode mode) {
  // [catch errors]
  mockingProgress.verificationStarted(new MockAwareVerificationMode(mock, mode));
  return mock;
}

然后,在处理所有mock调用的处理程序中,Mockito在验证开始后对mock的第一次调用开始验证:

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将假设您实际上正在调用该方法进行验证。请注意,如果调用略有不同,比如带有额外eq(5)verify(mockedThing).doB(eq(5), eq(mockedThing.doA()));,您会收到一条关于滥用匹配器的不同错误消息——特别是因为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 */);

Jeff Bowman的回答有助于解释发生了什么。

很多Mockito都是基于上次调用的函数,并且对mock的调用以与您尝试使用的语法不兼容的方式隐式检查状态。我在这里写了更多关于这个答案的内容Jeff Bowman昨天

相关内容

  • 没有找到相关文章

最新更新