Can anyone explain to me how hamcrest compares collections and and what the different methods from the library are for?
I am trying to understand how does IsIterableContainingInAnyOrder#containsInAnyOrder method work.
There are three overloaded methods in IsIterableContainingInAnyOrder class:
containsInAnyOrder(T... items)
containsInAnyOrder(Matcher<? super T>... itemMatchers)
containsInAnyOrder(java.util.Collection<Matcher<? super T>> itemMatchers)
My test case:
import org.hamcrest.Matchers;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertThat;
public class HamcrestCollections {
@Test
public void myTest(){
List<String> expected = Arrays.asList("one","two","four");
List<String> actual = Arrays.asList("two","one");
// This doesn't compile
assertThat(actual, Matchers.containsInAnyOrder(expected));
assertThat(actual, Matchers.containsInAnyOrder(expected.toArray()));
}
}
The first assertion doesn't compile, the error is:
Error:(19, 9) java: no suitable method found for assertThat(java.util.List<java.lang.String>,org.hamcrest.Matcher<java.lang.Iterable<? extends java.util.List<java.lang.String>>>)
method org.junit.Assert.<T>assertThat(java.lang.String,T,org.hamcrest.Matcher<? super T>) is not applicable
(cannot infer type-variable(s) T
(actual and formal argument lists differ in length))
method org.junit.Assert.<T>assertThat(T,org.hamcrest.Matcher<? super T>) is not applicable
(inferred type does not conform to upper bound(s)
inferred: java.util.List<java.lang.String>
upper bound(s): java.lang.Iterable<? extends java.util.List<java.lang.String>>,java.lang.Object)
I really do not catch what's going on in this message.
I found that it must be converted to the array in order to work (the second assertion in the example):
Matchers.containsInAnyOrder(expected.toArray()));
I suppose that in this case this method from the library is used:containsInAnyOrder(T... items)
, is that true ?
But how remaining methods from IsIterableContainingInAnyOrder
can be used ? Is there any way to compare collections without converting them to the array?
Your code compiles just fine in the first form with JDK 1.8.0_12, Hamcrest 1.3 and JUnit 4.12, although it does not produce the expected result due to a gotcha I'll be explaining below.
I can only guess that you may have a mix of library versions, or jdk, or something of the sorts. However, I believe it does not really matter, because of that gotcha I was mentioning.
Can anyone explain to me how hamcrest compares collections and and what the different methods from the library are for?
In short, you can either supply your own array/collection of matchers, or an array of items for which it will create matchers. Afterwards, the actual items are validated against the resulting matcher list.
If you check the sources you'll see that the IsIterableContainingInAnyOrder
constructor accepts a collection of matchers:
public IsIterableContainingInAnyOrder(Collection<Matcher<? super T>> matchers) {
this.matchers = matchers;
}
... while the methods you were wondering about are factory methods which return an IsIterableContainingInAnyOrder
instance. One is deprecated and I have skipped it. Then we have the following 2, where the first delegates to the second without anything fancy going on:
public static <T> Matcher<Iterable<? extends T>> containsInAnyOrder(Matcher<? super T>... itemMatchers) {
return containsInAnyOrder(Arrays.asList(itemMatchers));
}
public static <T> Matcher<Iterable<? extends T>> containsInAnyOrder(Collection<Matcher<? super T>> itemMatchers) {
return new IsIterableContainingInAnyOrder<T>(itemMatchers);
}
... and finally we have:
public static <T> Matcher<Iterable<? extends T>> containsInAnyOrder(T... items) {
List<Matcher<? super T>> matchers = new ArrayList<Matcher<? super T>>();
for (T item : items) {
matchers.add(equalTo(item));
}
return new IsIterableContainingInAnyOrder<T>(matchers);
}
As you see, a matcher is created for every item, which is somewhat a gotcha:
assertThat(actual, containsInAnyOrder("one", "two", "four"));
yelds:
java.lang.AssertionError:
Expected: iterable over ["one", "two", "four"] in any order
but: No item matches: "four" in ["two", "one"]
assertThat(actual, containsInAnyOrder(Arrays.asList("one", "two", "four")))
yelds:
java.lang.AssertionError:
Expected: iterable over [<[one, two, four]>] in any order
but: Not matched: "two"
Notice the subtle difference?
I found that it must be converted to the array in order to work (the second assertion in the example):
Matchers.containsInAnyOrder(expected.toArray()));
I suppose that in this case this method from the library is used:containsInAnyOrder(T... items)
, is that true ?But how remaining methods from
IsIterableContainingInAnyOrder
can be used ? Is there any way to compare collections without converting them to the array?
Just use the inline form as it was intended: assertThat(myList, containsInAnyOrder("one", "two", "four"))
. I suppose this offers better readability and avoids unnecessary variables or superfluous coding, such as the expected collection. Usually you need to check for a few items, not hundreds, right?