ChatGPT解决这个技术问题 Extra ChatGPT

如何验证具有不同参数的多个方法调用

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

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()

B
Brad

进一步阅读使我尝试使用 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<String> 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 在内部调用了两次。
C
Christoph Walesch

如果两个 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 并断言输入集等于参数捕获给定的集。
J
John B

尝试这样的事情:

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

你的支票显然太宽松了。
s
sendon1982

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

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

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

B
Brice

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

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 类型。

[编辑@布拉德]

在我的 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
e
epox

OP 代码正确(检查您的总计)

=1= 告诉 Mokito 总呼叫期望。

=2= 告诉 Mokito 每个参数组合预期的次数。 (Mokito 假设 times(1),如果 times 被省略)。

verify(errors, times(2)).add(any(), any(ActionMessage.class));

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

操作码正确;它检查你需要什么。

您的问题出在您的 Prod 代码中,该代码(似乎)从未使用 ActionError arg 类型调用第一个 arg 组合。所以Mokito正确地抱怨了。但是(我同意)投诉消息对于多个电话来说是令人困惑的。

解决方案:确保(首先)您确实准确地调用了该方法 2 次(使用任何参数)。


m
marc_s

与@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)