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

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

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()的调用是否使用“ exction.message”调用,以及是否使用“ exction.Details”调用

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

然而莫基托抱怨如下

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 检查这两个值?

118006 次浏览

试试这样:

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

你的代码可能有问题,因为事实上你写了这样的代码:

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

注意,对于实际调用,第一个验证甚至不是按顺序进行的。

另外,我建议你不要嘲笑你没有的类型(如支柱类型)。

[编辑@布拉德]

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

所以我的问题的答案是

/**
* 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));

进一步的阅读使我尝试使用 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"));

如果两个 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));

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

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


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

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

Mockito.times(wantedInvocationCount)

以确保方法被调用的次数是准确的(我认为这是一个更好的解决方案)

Mockito.verifyNoMoreInteractions(mock)

以确保模拟不会在任何上下文中被进一步使用。完整示例:

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


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


Mockito.verifyNoMoreInteractions(mockObject)

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

= 1 = 告诉 Mokito 整个通话期望。

= 2 = 告诉 Mokito 每个参数组合的预期次数(如果省略了 时间,Mokito 假设为 乘以(1))。

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


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

OP 代码是正确的; 它检查您需要什么。

你的问题是在你的 Prod 代码,它(似乎)从来没有调用与 动作错误参数类型的第一个参数组合。所以 Mokito 正确地投诉了。然而(我同意)投诉信息对于多个电话来说是混乱的。

解决方案: 确保(首先)精确地调用该方法2次(使用任意参数)。