160

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?

Matt Ke
  • 3,599
  • 12
  • 30
  • 49
Brad
  • 15,186
  • 11
  • 60
  • 74

7 Answers7

137

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"));
Brad
  • 15,186
  • 11
  • 60
  • 74
  • is there a way to make sure that certain parameters were paired using this approach? Say for example the OP's method had two arguments and wanted to verify they were called together – committedandroider Jun 03 '19 at 20:45
  • 1
    The OP's test case calls `methodToTest()` exactly once, therefore this answer does verify that the two calls are made together. The captured `List values` that is being asserted will only contain the two values being tested and no others. You could also add `assertTrue(values.size == 2)`. If this is what you want I would replace the 3 assertTrue statements with a single [Hamcrest](http://hamcrest.org/JavaHamcrest/javadoc/2.1/org/hamcrest/Matchers.html) ... `assertThat(values, contains("exception.message", "exception.detail"));` – Brad Jun 04 '19 at 14:05
  • doesn't OP's test case call methodToTest() twice? – committedandroider Jun 04 '19 at 18:05
  • sorry I wasn't clear. I was referring to the scenario where OP wanted to test that two arguments were called in conjunction. So the method signature would look something like public void methodToTest( Exception e, Message m, ActionErrors errors ) { so that a specific exception gets called with a specific message. I supposed that you could just have two ArgumentCaptors and then retrieve the index and compare using the values at those indexes in both values lists – committedandroider Jun 04 '19 at 18:08
  • OP's test case calls `methodToTest()` once. It is the method argument `ActionErrors errors` is internally called twice. – Brad Jun 05 '19 at 15:00
77

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));
Christoph Walesch
  • 2,337
  • 2
  • 32
  • 44
  • 7
    It is enough to pass single `errors` argument: `InOrder inOrder = inOrder(errors);` (see [docs](http://javadoc.io/page/org.mockito/mockito-core/latest/org/mockito/Mockito.html#in_order_verification)) – GreenhouseVeg Nov 30 '17 at 15:31
  • 2
    What if the order is NOT relevant? which is often the case. – haelix Dec 04 '18 at 15:01
  • 1
    @haelix In that case, use Brads answer. Convert the `List` to `Set` and assert that the Set of inputs equals the set given by the argument captures. – flopshot Feb 15 '19 at 21:44
26

Try something like this:

verify(errors, times(2))
     .add(AdditionalMatchers.or(eq("exception.message"), eq("exception.detail")),
          any(ActionError.class));
John B
  • 32,493
  • 6
  • 77
  • 98
22

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));
sendon1982
  • 9,982
  • 61
  • 44
19

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));
bric3
  • 40,072
  • 9
  • 91
  • 111
  • 1
    Don`t get what you are trying to say. Is order of verification matters? if verification order do matters. Why then here is InOrder api provided? – Oleksandr Papchenko Nov 04 '16 at 13:04
  • Just like what is written above verification order is irrelevant ; that's why there's `InOrder`. – bric3 Nov 04 '16 at 13:08
10

OP code is correct (check your Total)

=1= Tell Mokito the total call expectation.

=2= Tell Mokito how many times each parameter combination was expected. (Mokito assumes times(1), if times is omitted).

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

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

The OP code is correct; it checks what you need.

Your problem was in your Prod code, which (seems) never called the first arg combination with ActionError arg type. So Mokito correctly complained. However (I agree) the complaint message is confusing for the multiple calls.

SOLUTION: Ensure (first of all) you really called the method precisely 2 times (with any args).

epox
  • 9,236
  • 1
  • 55
  • 38
4

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)
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Michał Powłoka
  • 1,424
  • 1
  • 13
  • 31