How to verify multiple method calls with different params

JavaMockito

Java Problem Overview


I have the following method that I wish to verify behaviour on.

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

	...
}

In my @Test class I was hoping to do something like this to verify that errors.add() is called with "exception.message" and again with "exception.detail"

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

however Mockito complains as follows

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

How can I tell Mockito to check for both values?

Java Solutions


Solution 1 - Java

Further reading has led me to try using ArgumentCaptors and the following works, although much more verbose than I would like.

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

Solution 2 - Java

If the order of both add() calls is relevant, you can use 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));

Solution 3 - Java

Try something like this:

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

Solution 4 - Java

you probably have a problem in your code. Because as a matter of fact you actually write this code:

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

Note the first verify is not even in order in regard of the actual invocations.

Also, I would recommand you to actually don't mock types you don't own, eg the struts type.

[EDIT @Brad]

After running Brice's code (above) in my IDE I can see that I have used ActionError instead of ActionMessage, so that is why my verify() was not matching. The error message I initially posted was misleading me into thinking it was the first argument that was not matching. It turns out it was the second argument.

So the answer to my question is

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

Solution 5 - Java

You can use Mockito.atLeastOnce() which allows Mockito to pass the test even if that mockObject will be called many times.

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

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

Solution 6 - Java

  1. Tell Mokito the total calls expectation.

  2. Tell Mokito how many times each parameter combination was expected.

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

Solution 7 - Java

In a similar way to @sendon1928 we can use:

Mockito.times(wantedInvocationCount)

to make sure method was called exact number of times (preferable solution in my opinion). Afterwards, we can call

Mockito.verifyNoMoreInteractions(mock)

To make sure that mock was not used further in any context. Full example:

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

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

Mockito.verifyNoMoreInteractions(mockObject)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBradView Question on Stackoverflow
Solution 1 - JavaBradView Answer on Stackoverflow
Solution 2 - JavaChristoph WaleschView Answer on Stackoverflow
Solution 3 - JavaJohn BView Answer on Stackoverflow
Solution 4 - JavaBriceView Answer on Stackoverflow
Solution 5 - Javasendon1982View Answer on Stackoverflow
Solution 6 - JavaepoxView Answer on Stackoverflow
Solution 7 - JavaMichał PowłokaView Answer on Stackoverflow