Mockito and Hamcrest: how to verify invocation of Collection argument?

Solution 1:

You can just write

verify(service).perform((Collection<String>) Matchers.argThat(contains("a", "b")));

From the compiler's point of view, this is casting an Iterable<String> to a Collection<String> which is fine, because the latter is a subtype of the former. At run time, argThat will return null, so that can be passed to perform without a ClassCastException. The important point about it is that the matcher gets onto Mockito's internal structure of arguments for verification, which is what argThat does.

Solution 2:

As an alternative one could change the approach to ArgumentCaptor:

@SuppressWarnings("unchecked") // needed because of `List<String>.class` is not a thing
// suppression can be worked around by using @Captor on a field
ArgumentCaptor<List<String>> captor = ArgumentCaptor.forClass(List.class);

verify(service).perform(captor.capture());
assertThat(captor.getValue(), contains("a", "b"));

Notice, that as a side effect this decouples the verification from the Hamcrest library, and allows you to use any other library (e.g. Truth):

assertThat(captor.getValue()).containsExactly("a", "b");

Solution 3:

If you get stuck in situations like these, remember that you can write a very small reusable adapter.

verify(service).perform(argThat(isACollectionThat(contains("foo", "bar"))));

private static <T> Matcher<Collection<T>> isACollectionThat(
    final Matcher<Iterable<? extends T>> matcher) {
  return new BaseMatcher<Collection<T>>() {
    @Override public boolean matches(Object item) {
      return matcher.matches(item);
    }

    @Override public void describeTo(Description description) {
      matcher.describeTo(description);
    }
  };
}

Note that David's solution above, with casting, is the shortest right answer.

Solution 4:

You can put your own lambda as an ArgumentMatcher

when(myClass.myMethod(argThat(arg -> arg.containsAll(asList(1,2))))
    .thenReturn(...);

Solution 5:

Why not just verify with the expected arguments, assuming the list only contains the two items, e.g.:

final List<String> expected = Lists.newArrayList("a", "b");
verify(service).perform(expected);

Whilst I agree with Eugen in principle, I think that relying on equals for String comparison is acceptable... besides, the contains matcher uses equals for comparison anyway.