【问题标题】:Mockito: multiple calls to the same methodMockito:多次调用同一个方法
【发布时间】:2012-11-08 15:06:39
【问题描述】:

我正在使用 Mockito 模拟一个对象,该对象上的相同方法被多次调用,我希望每次都返回相同的值。
这就是我所拥有的:

LogEntry entry = null; // this is a field
// This method is called once only.
when(mockLogger.createNewLogEntry()).thenAnswer(new Answer<LogEntry>() {
  @Override
  public LogEntry answer(InvocationOnMock invocationOnMock) throws Throwable {
    entry = new LogEntry();
    return entry;
  }
});
// This method can be called multiple times, 
// If called after createNewLogEntry() - should return initialized entry.
// If called before createNewLogEntry() - should return null.
when(mockLogger.getLogEntry()).thenAnswer(new Answer<LogEntry>() {
  @Override
  public LogEntry answer(InvocationOnMock invocationOnMock) throws Throwable {
    return entry;
  }
});

问题是,我的 getLogEntry 方法似乎只被调用了一次。对于所有后续调用,null 会改为返回,我会在测试中获得 NPE。
如何告诉 mockito 对所有调用使用存根版本?

================================================ ==================
为后代验尸

我做了一些额外的调查,一如既往,这不是图书馆的错,而是我的错。在我的代码中,在调用createNewLogEntry() 之前调用getLogEntry() 的方法之一。 NPE 绝对合法,测试实际上是在我的代码中发现了一个错误,而不是我在 Mockito 中发现了错误。

【问题讨论】:

  • 相当神秘。通常,这应该有效。我想我会提出几个问题:1)你确定它是空的条目,而不是以某种方式将 mockLogger 重置为空吗? 2)是否有可能在没有模拟方法的情况下重新创建 mockLogger?在定义它的方法(设置与测试方法)中构建上述代码可能会有所帮助。最后,您可以在答案 impl(或断点)中尝试 println,以确保实际执行的是什么。
  • 试图重现您的问题,使用 Mockito 1.9.5 的类似方法适用于我 ==> 也许您可以通过 Mockito 邮件列表发送整个 Test 类的代码?

标签: java unit-testing junit mocking mockito


【解决方案1】:

您的存根应该可以按您的意愿工作。来自Mockito doc

一旦被存根,该方法将始终返回存根值 它被调用了多少次。

【讨论】:

    【解决方案2】:

    除非我遗漏了什么,如果你想为每个方法调用返回相同的对象,那么为什么不简单呢:

    final LogEntry entry = new LogEntry()
    when(mockLogger.createNewLogEntry()).thenReturn(entry);
    when(mockLogger.getLogEntry()).thenReturn(entry);
    
    ...
    
    verify(mockLogger).createNewLogEntry();
    verify(mockLogger, times(???)).getLogEntry();
    

    Mockito 将为每个匹配的调用返回相同的值。

    【讨论】:

    • 如果首先调用 createNewLogEntry(),那么所有后续调用 getLogEntry() 都应该返回初始化的条目。如果未首先调用 createNewLogEntry(),则对 getLogEntry() 的所有后续调用都应返回 null。你的提议不会以这种方式发挥作用。
    • 如果可能的话,你能把它分成两个单独的测试用例吗?一个 createNewLogEntry 被称为另一个而不是?这可能会简化事情。
    • 我相信你的意思是thenReturn,而不是thenAnswer
    【解决方案3】:

    我错过了什么,还是以下就足够了?

    LogEntry entry = null; // this is a field
    when(mockLogger.createNewLogEntry()).thenAnswer(new Answer<LogEntry>() {
      @Override
      public LogEntry answer(InvocationOnMock invocationOnMock) throws Throwable {
        if (entry == null) {
          entry = new LogEntry();
        }
        return entry;
      }
    });
    when(mockLogger.getLogEntry()).thenAnswer(new Answer<LogEntry>() {
      @Override
      public LogEntry answer(InvocationOnMock invocationOnMock) throws Throwable {
        return entry;
      }
    });
    

    仅在entry == null 时进行分配。

    【讨论】:

    • createNewLogEntry() 只被调用一次。之后可以多次调用 getLogEntry() ,但在返回默认值 null 之后,似乎只调用了一次模拟版本。
    • 似乎发生了一些奇怪的事情。每次调用该方法时,Mockito 都应该并且确实调用 Answer。你确定你没有在代码/测试中的某个地方重置模拟或条目吗?
    • 我检查了,我设置 entry = null 的唯一地方是在 setUp() 方法中,如上所示。我不会在测试中的任何地方重置它。我会做更多的调试,但据我所知,getLogEntry() 的答案只被调用一次。我明天会调查更多。
    猜你喜欢
    • 1970-01-01
    • 2011-12-26
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    • 2015-10-02
    • 1970-01-01
    • 2020-04-17
    相关资源
    最近更新 更多