Hamcrest equal collections
-
21-09-2019 - |
Question
Is there a matcher in Hamcrest to compare collections for equality?
There is contains
and containsInAnyOrder
but I need equals
not bound to concrete collection type.
E.g. I cannot compare Arrays.asList and Map.values with Hamcrest equals
.
Thanks in advance!
Solution
I cannot compare Arrays.asList and Map.values with Hamcrest equals.
This is because of hamcrest's over-zealous type signatures. You can do this equality comparison, but you need to cast the List
object to Collection
before it'll compile.
I often have to do casting with Hamcrest, which feels wrong, but it's the only way to get it to compile sometimes.
OTHER TIPS
Casting to a Collection may work, but it makes some assumptions about the underlying Collection implementations (e.g., order?). A more general approach would be to write your own matcher.
Here's a nearly complete matcher implementation that does what you want (you'll need to fill in the imports and describeTo method). Note that this implementation requires all elements of two collections to be equal, but not necessarily in the same order.
public class IsCollectionOf<T> extends TypeSafeMatcher<Collection<T>> {
private final Collection<T> expected;
public IsCollectionOf(Collection<T> expected) {
this.expected = expected;
}
public boolean matchesSafely(Collection<T> given) {
List<T> tmp = new ArrayList<T>(expected);
for (T t : given) {
if (!tmp.remove(t)) {
return false;
}
return tmp.isEmpty();
}
// describeTo here
public static <T> Matcher<Collection<T>> ofItems(T... items) {
return new IsCollectionOf<T>(Arrays.asList(items));
}
}
If you have trouble with the equals method of the collections implementation, you might also first copy the collections:
assertThat( new ArrayList<Whatever>(actual), equalTo( new ArrayList<Whatever>(expected) );
Also the following might work:
assertThat(actual, both(everyItem(isIn(expected))).and(containsInAnyOrder(expected)));