JavaHamcrest
JavaHamcrest copied to clipboard
empty()-Matcher in contains()-Matcher works not with custom class
I have a List of Lists and for that I wanted to check the contents. Therefore, I nested Matchers.empty() and Matchers.contains() inside Matchers.contains() - but the test failed, saying that the first element should have been empty, but was... empty. Well...
I've put together a short test-case, showing different related scenarios that are working (nested empty() with no contains() afterwards and vice versa) and the szenario not working. Interestingly, it does work with String, but not with a custom class.
@Test
public void emptyTest() throws Exception {
List<List<Foo>> listOfLists = new ArrayList<>();
Foo foo = new Foo();
listOfLists.add(new ArrayList<>());
// works as expected
assertThat("Just empty()",listOfLists,contains(empty()));
listOfLists.clear();
listOfLists.add(Arrays.asList(foo));
// works as expected
assertThat("Just non-empty List",listOfLists,contains(contains(foo)));
listOfLists.clear();
listOfLists.add(new ArrayList<>());
listOfLists.add(Arrays.asList(foo));
// works as expected
assertThat("first list is empty, check explicitly", listOfLists.get(0), empty());
// works as expected - but why do I need the collection type?
assertThat("empty and non-empty mixed, using emptyCollectionOf(Foo.class)",listOfLists,contains(emptyCollectionOf(Foo.class),contains(foo)));
// fails, as empty() doesn't recognize the collection is empty - but only using a custom class... for List<List<String>> it works...
assertThat("empty and non-empty mixed, using empty()",listOfLists,contains(empty(),contains(foo)));
}
class Foo{
@Override
public String toString() {
return "Foo";
}
}
Yielding following Message:
java.lang.AssertionError: empty and non-empty mixed, using empty()
Expected: iterable containing [<an empty collection>, <iterable containing [<Foo>]>]
but: item 0: was <[]>
I'm using JUnit 4.12 Hamcrest 1.3 and Java 8.
It's hard for me to tell, but it looks like this is happening because java can't figure out the generic types. If I change the last line to:
assertThat("empty and non-empty mixed, using empty()",listOfLists,contains(Matchers.<Foo>empty(),Matchers.<Foo>contains(foo)));
Then the test passes. My IDE suggests that it may be the generic type on empty() that is the main culprit. When I remove the explicit generic type, its type hinting for the outer contains()
goes from Iterable<? super List<Foo>>
to Iterable<?>
. I think, based on that, these matchers are getting passed to the contains(Object... elements)
overload, which tries to see if the elements of the list are equal to the matchers, rather than checking using the matchers themselves.
I'm really not sure how to address the problem.
I agree with @brownian-motion, the test works for me if I include the explicit types (which is ugly). I'm afraid I don't have any better advice than to swear at the Java generics implementation right now :-(
Hi @tumbarumba, I would like to work on this issue and I have submitted a pull request.