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


114

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

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


1
当您有两种具有不同签名的方法时,可以为两种方法编写单独的测试用例。
Naveen Babu

8
是的,但是在这种情况下,其方法签名相同,但参数值不同
Brad

您可以尝试使用Mockito.reset()
takacsot,

Answers:


101

进一步的阅读使我尝试使用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的方法有两个参数,想要验证它们是否被一起调用
提交的

1
OP的测试用例仅调用methodToTest()一次,因此该答案确实验证了两个调用是否一起进行。List<String> values被断言的捕获将仅包含两个正在测试的值,而没有其他值。您还可以添加assertTrue(values.size == 2)。如果这是您想要的,我将用一个Hamcrest替换这3条assertTrue语句...assertThat(values, contains("exception.message", "exception.detail"));
Brad

OP的测试用例不会两次调用methodToTest()吗?
committedandroider

对不起,我不清楚。我指的是OP想要测试两个参数一起调用的情况。因此,方法签名看起来类似于public void methodToTest(Exception e,Message m,ActionErrors errors){,以便使用特定消息调用特定异常。我猜想,你可能只是有两个ArgumentCaptors,然后检索索引和使用这两个值列表在这些指标的值进行比较
committedandroider

OP的测试用例仅调用methodToTest()一次。它是方法参数ActionErrors errors在内部被两次调用。
Brad

60

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

7
传递一个errors参数就足够了: InOrder inOrder = inOrder(errors);(see docs
GreenhouseVeg

2
如果订单不相关怎么办?这是经常的情况。
haelix

1
@haelix在这种情况下,请使用Brads答案。将转换为ListSet并断言输入的集合等于参数捕获给出的集合。

25

尝试这样的事情:

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

4
您的支票显然太宽松了。
haelix

17

您的代码可能有问题。因为实际上您实际上是在编写以下代码:

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

1
听不懂你想说的话。验证顺序重要吗?如果验证顺序很重要。为什么然后在这里提供InOrder api?
Oleksandr Papchenko '16

就像以上验证顺序所写的无关紧要; 这就是为什么InOrder
布莱斯

12

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

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

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

1

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

0

与@ 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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.