When working with Mockito and collections, Hamcrest matchers are commonly used to verify the contents of collections passed as arguments. However, there are alternatives and additional strategies you can employ:
1. Argument Captors**
One alternative to using Hamcrest matchers is to use Mockito's `ArgumentCaptor`. This allows you to capture the actual argument passed to a method and then assert its contents using standard Java assertions or other assertion libraries like JUnit or AssertJ.java
// Example using ArgumentCaptor
ArgumentCaptor> captor = ArgumentCaptor.forClass(Collection.class);
verify(service).perform(captor.capture());
Collection capturedCollection = captor.getValue();
assertTrue(capturedCollection.contains("a"));
assertTrue(capturedCollection.contains("b"));
2. Mockito's Built-in Matchers**
Mockito provides its own set of matchers, such as `any()`, `eq()`, and `anyList()`, which can be used for simple cases. However, these are less flexible than Hamcrest matchers for complex assertions.java
// Using Mockito's anyList() matcher
verify(service).perform(anyList());
3. AssertJ**
AssertJ is another assertion library that provides fluent and readable assertions. It can be used in conjunction with Mockito to assert the contents of collections. AssertJ offers more expressive assertions than JUnit and can be a good alternative to Hamcrest for some use cases.java
// Example using AssertJ
ArgumentCaptor> captor = ArgumentCaptor.forClass(Collection.class);
verify(service).perform(captor.capture());
Collection capturedCollection = captor.getValue();
assertThat(capturedCollection).contains("a", "b");
4. Custom Matchers**
If you frequently need to assert specific properties of collections, you can create custom matchers using Hamcrest's `TypeSafeMatcher` or AssertJ's custom assertions. This allows you to encapsulate complex logic into reusable matchers.java
// Creating a custom matcher with Hamcrest
public class ContainsInOrder extends TypeSafeMatcher> {
private final List expected;
public ContainsInOrder(List expected) {
this.expected = expected;
}
@Override
protected boolean matchesSafely(Collection actual) {
Iterator actualIterator = actual.iterator();
for (String expectedItem : expected) {
if (!actualIterator.hasNext() || !actualIterator.next().equals(expectedItem)) {
return false;
}
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText("contains in order: ").appendValueList("[", ", ", "]", expected);
}
public static Matcher> containsInOrder(List expected) {
return new ContainsInOrder(expected);
}
}
// Usage
verify(service).perform(argThat(containsInOrder(Arrays.asList("a", "b"))));
Conclusion
While Hamcrest matchers are powerful for asserting collections, using Argument Captors, Mockito's built-in matchers, AssertJ, or creating custom matchers can provide alternatives or complementary approaches depending on your specific testing needs.Citations:
[1] https://java.libhunt.com/mockito-alternatives
[2] https://stackoverflow.com/questions/20441594/mockito-and-hamcrest-how-to-verify-invocation-of-collection-argument
[3] https://www.javacodegeeks.com/2015/11/hamcrest-matchers-tutorial.html
[4] https://stackshare.io/mockito/alternatives
[5] https://hamcrest.org/JavaHamcrest/tutorial
[6] https://stackoverflow.com/questions/8348046/mockitos-matcher-vs-hamcrest-matcher
[7] https://rust.libhunt.com/mockito-alternatives
[8] https://www.reddit.com/r/java/comments/nj7dvd/who_here_are_using_the_hamcrest_api_and_why/
[9] https://www.digitalocean.com/community/tutorials/mockito-argument-matchers-any-eq