Search code examples
javamockitohamcrest

How to assertThat superclass list equal to subclass matcher using hamcrest and mockito


I am trying to do the following.

final Matcher<SuperClass> matcher1 = Matchers.hasProperty("a", equalTo("b"));
final Matcher<SuperClass> matcher2 = Matchers.hasProperty("c", equalTo("d"));
final Matcher<SuperClass> matchers = Matchers.allOf(matcher1, matcher2);

List<SubClass> list = someStuff();

assertThat(list, everyItem(matchers));

I am getting a compilation error on the assert line, is there any easy way to get rid of this.


Solution

  • Option 1:

    @SuppressWarnings({"unchecked", "rawtypes"})
    @Test public void yourTest() {
        final Matcher<SuperClass> matcher1 = Matchers.hasProperty("a", equalTo("b"));
        final Matcher<SuperClass> matcher2 = Matchers.hasProperty("c", equalTo("d"));
        final Matcher<SuperClass> matchers = Matchers.allOf(matcher1, matcher2);
        List<SubClass> list = someStuff();
        // Note cast to raw type here, corresponding to the suppressed warnings.
        assertThat(list, (Matcher) everyItem(matchers));
    }
    

    Option 2:

    // These are all of type Matcher<SubClass> instead.
    final Matcher<SubClass> matcher1 = Matchers.hasProperty("a", equalTo("b"));
    final Matcher<SubClass> matcher2 = Matchers.hasProperty("c", equalTo("d"));
    final Matcher<SubClass> matchers = Matchers.allOf(matcher1, matcher2);
    List<SubClass> list = someStuff();
    assertThat(list, everyItem(matchers));
    

    Why? Java's generics handling is too clever for Hamcrest. everyItem(matchers) will return a Matcher<Iterable<SuperClass>>, but you don't have an Iterable<SuperClass>, you have an Iterable<SubClass>. It would all be fine if you actually had your Matcher methods produce a Matcher<Iterable<? extends SuperClass>>, but it's nearly impossible for you to convince Java that your matcher1 and matcher2 are compatible with one another.