【问题标题】:How to verify multiple method calls with different params如何验证具有不同参数的多个方法调用
【发布时间】:2011-12-14 11:57:52
【问题描述】:

我有以下方法希望验证其行为。

public void methodToTest(Exception e, ActionErrors errors) {
    ...

    errors.add("exception.message", 
            ActionMessageFactory.createErrorMessage(e.toString()));

    errors.add("exception.detail",
            ActionMessageFactory.createErrorMessage(e.getStackTrace()[0].toString()));

    ...
}

在我的@Test 课程中,我希望做这样的事情来验证errors.add() 是用“exception.message”调用的,然后又是用“exception.detail”调用的

verify(errors).add(eq("exception.message"), any(ActionError.class));
verify(errors).add(eq("exception.detail"), any(ActionError.class));

然而 Mockito 抱怨如下

Argument(s) are different! Wanted:
actionErrors.add(
    "exception.message",
    <any>
);

Actual invocation has different arguments:
actionErrors.add(
    "exception.detail",
    org.apache.struts.action.ActionError@38063806
);

如何让 Mockito 检查这两个值?

【问题讨论】:

  • 当你有 2 个签名不同的方法时,你可以为这两个方法编写单独的测试用例。
  • 是的,但在这种情况下,它的方法签名相同,只是参数值不同
  • 你可以尝试使用Mockito.reset()

标签: java mockito


【解决方案1】:

进一步阅读使我尝试使用 ArgumentCaptors 和以下作品,尽管比我想要的要冗长得多。

ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);

verify(errors, atLeastOnce()).add(argument.capture(), any(ActionMessage.class));

List<String> values = argument.getAllValues();

assertTrue(values.contains("exception.message"));
assertTrue(values.contains("exception.detail"));

【讨论】:

  • 有没有办法确保使用这种方法对某些参数进行配对?比如说,OP 的方法有两个参数,并且想验证它们是一起调用的
  • OP 的测试用例只调用了一次 methodToTest(),因此这个答案确实验证了这两个调用是一起进行的。正在断言的捕获的List&lt;String&gt; values 将仅包含正在测试的两个值,而没有其他值。您也可以添加assertTrue(values.size == 2)。如果这是您想要的,我会用一个 Hamcrest ... assertThat(values, contains("exception.message", "exception.detail")); 替换 3 个 assertTrue 语句
  • OP的测试用例不会调用methodToTest()两次吗?
  • 对不起,我不清楚。我指的是OP想要测试两个参数被同时调用的场景。所以方法签名看起来像 public void methodToTest(Exception e, Message m, ActionErrors errors) { 以便使用特定消息调用特定异常。我认为你可以只有两个 ArgumentCaptors,然后检索索引并使用两个值列表中这些索引处的值进行比较
  • OP 的测试用例调用methodToTest() 一次。就是方法参数ActionErrors errors在内部被调用了两次。
【解决方案2】:

如果两个add()调用的顺序相关,你可以使用InOrder

InOrder inOrder = inOrder(errors);
inOrder.verify(errors).add(eq("exception.message"), any(ActionError.class));
inOrder.verify(errors).add(eq("exception.detail"), any(ActionError.class));

【讨论】:

  • 传递单个errors 参数就足够了:InOrder inOrder = inOrder(errors);(见docs
  • 如果订单不相关怎么办?情况经常如此。
  • @haelix 在这种情况下,请使用 Brads 回答。将List 转换为Set 并断言输入集等于参数捕获给定的集。
【解决方案3】:

试试这样的:

verify(errors, times(2))
     .add(AdditionalMatchers.or(eq("exception.message"), eq("exception.detail")),
          any(ActionError.class));

【讨论】:

  • 你的支票显然太宽松了。
【解决方案4】:

您的代码可能有问题。因为事实上你实际上写了这段代码:

Map<Character, String> map = mock(Map.class);

map.put('a', "a");
map.put('b', "b");
map.put('c', "c");

verify(map).put(eq('c'), anyString());
verify(map).put(eq('a'), anyString());
verify(map).put(eq('b'), anyString());

请注意,就实际调用而言,第一次验证甚至不是按顺序排列的。

另外,我建议你不要模拟你不拥有的类型,例如 struts 类型。

[编辑@Brad]

在我的 IDE 中运行 Brice 的代码(上图)后,我可以看到我使用的是 ActionError 而不是 ActionMessage,所以这就是我的 verify() 不匹配的原因。我最初发布的错误消息误导我认为这是第一个不匹配的参数。原来是第二个论点。

所以我的问题的答案是

/** 
 * note that ActionMessageFactory.createErrorMessage() returns ActionMessage
 * and ActionError extends ActionMessage
 */
verify(errors).add(eq("exception.message"), any(ActionMessage.class));
verify(errors).add(eq("exception.detail"), any(ActionMessage.class));

【讨论】:

  • 不要明白你想说什么。验证顺序重要吗?如果验证顺序很重要。那为什么这里提供了 InOrder api?
  • 就像上面写的验证顺序无关;这就是为什么会有InOrder
【解决方案5】:

您可以使用Mockito.atLeastOnce(),它允许 Mockito 通过测试,即使该 mockObject 将被多次调用。

Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(1));

Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(2));

【讨论】:

    【解决方案6】:

    1) 告诉 Mokito 总通话预期。

    2) 告诉 Mokito 每个参数组合预期的次数。

    verify(errors, times(2)).add(any(), any(ActionMessage.class));
    
    verify(errors, atLeastOnce()).add(eq("exception.message"), any());
    verify(errors, atLeastOnce()).add(eq("exception.detail"), any());
    

    【讨论】:

      【解决方案7】:

      与@sendon1928 类似,我们可以使用:

      Mockito.times(wantedInvocationCount)
      

      确保方法被调用的次数准确(在我看来是最好的解决方案)。之后,我们可以调用

      Mockito.verifyNoMoreInteractions(mock)
      

      确保在任何情况下都不会进一步使用 mock。完整示例:

      Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(1));
      
      Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(2));
      
      Mockito.verifyNoMoreInteractions(mockObject)
      

      【讨论】:

        猜你喜欢
        • 2017-09-06
        • 2011-10-14
        • 2011-07-11
        • 2021-11-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-10-16
        相关资源
        最近更新 更多