当前位置: 首页 > 面试题库 >

无法在验证参数中使用模拟函数调用:调用过多

翟修明
2023-03-14
问题内容

设置如下:

//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而不是将对方法的反射引用传递给doAMockito。这根本与在存根或验证过程中调用同一模拟程序的语法不兼容。

我之前已经针对Matchers撰写过有关此文章的文章,在存根期间也有同样的问题。html" target="_blank">遍历源代码,至少在同一个模拟中调用方法时,您会看到相同的验证问题。

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

  1. 致电verify(mockedThing)
  2. 如有必要,请按顺序呼叫匹配器。不要在上调用任何方法mockedThing
  3. mockedThing如果您不使用匹配器,则使用实际参数值调用您要验证的方法,如果您使用匹配器,则使用虚拟(忽略)参数值调用。由于持续的Mockito跟踪匹配器在后台堆栈,匹配方法可以返回0null没有思想的Mockito那些值对证。

在幕后

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

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

然后,在处理所有模拟调用的 处理程序
内部
,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]
}

因此,如果您仅与模拟进行交互以获取参数值,则Mockito将假定您实际上是在调用方法进行验证。请注意,如果调用稍有不同(如verify(mockedThing).doB(eq(5),eq(mockedThing.doA()));extra一样)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 */);


 类似资料:
  • 出于学校目的,我正在创建一个使用股票API的应用程序。 我正在尝试为一种获取过去10年所有股票数据的方法编写一个测试。我不想实际获取所有这些数据,而是想抛出一个异常。 我想测试的方法: 股票时间系列(....)调用可以抛出阿尔法仓位异常。 我这样嘲弄了TimeSeries类: 在我的测试类中,我想模拟这个调用,并返回一个异常而不是实际数据。 无论我多么试图嘲笑这段代码,它永远不会抛出异常。它将始终

  • 问题内容: 我有以下代码要进行单元测试: 我想,以验证上的第一次调用 返回。 因此,我无法使用,因为那时该方法已经完成并被调用。 因此,我认为我需要在调用发生时已经对调用进行验证。如何使用Mockito做到这一点? 问题答案: 我创建了以下实现: 此答案捕获正在进行的调用的属性。然后可以将其用于简单的断言。该实现使用Java 8 API。如果那不可用,则需要使用能够将转换为捕获值的接口。测试用例中

  • 我试图模拟对resttemplate.exchange()的调用,但无法使其工作。当前,对exchange()的调用挂起,所以我认为正在调用的是实际的方法,而不是我的模拟方法。对exchange()的调用如下:

  • 我是java / kotlin函数式编程的新手,并做了一些练习。只是想知道如何在调用它时传递lambda的参数。例如,在这里调用一些方法,我需要传递一个Int参数。 如何做到这一点?答案可能非常简单,我只是没有在任何地方找到它,在文档中只有列表的例子,在这种情况下答案是清楚的:

  • 我试图在测试中模拟一个调用,但我得到了一个错误,因为它调用了真正的方法,而不是模拟它。 这是我的方法 } 这是我的测试课 测试实际上调用了受保护的方法config Setter,并在设置代理时失败。帮助我理解我在这里做错了什么。

  • 问题内容: 我正在寻找一种与Mockito进行验证的方法,即在测试过程中与给定的模拟没有任何交互。对于具有验证模式的给定方法,很容易实现这一点,但是我还没有找到完整模拟的解决方案。 我真正想要实现的是:在测试中验证,没有任何内容打印到控制台上。jUnit的总体思路如下: A 有很多方法,我真的不想用单独的验证方法来验证每个方法,而… 因此,我希望,如果有一个简单的解决方案,鉴于我具有良好的测试覆盖