Search code examples
javaunit-testingjunithamcrest

Why is assertThat(map1, sameInstance(map2)) not allowed?


With the new Assert grammar, when testing for identity, one would write

Assert.assertThat(obj1, CoreMatchers.sameInstance(obj2))

instead of

Assert.assertSame(obj1, obj2)

I am trying assert the identity of a map. So I write

Assert.assertThat(map1, CoreMatchers.sameInstance(map2))

where map is of type HashMap<String,String> But my tests fail at compile time:

Error:(33, 9) error: no suitable method found for assertThat(Map,Matcher<Map<String,String>>)
method Assert.<T#1>assertThat(String,T#1,Matcher<? super T#1>) is not applicable
(cannot infer type-variable(s) T#1
(actual and formal argument lists differ in length))
method Assert.<T#2>assertThat(T#2,Matcher<? super T#2>) is not applicable
(cannot infer type-variable(s) T#2
(argument mismatch; Matcher<Map<String,String>> cannot be converted to Matcher<? super Map>))
where T#1,T#2 are type-variables:
T#1 extends Object declared in method <T#1>assertThat(String,T#1,Matcher<? super T#1>)
T#2 extends Object declared in method <T#2>assertThat(T#2,Matcher<? super T#2>)
Error:(33) error: no suitable method found for assertThat(Map,Matcher<Map<String,String>>)

Why can't JUnit (or Hamcrest) figure out which matcher to use?


Solution

  • Turns out this had nothing to do with asserting the identity of the map - the code was correct - but it does have something to do with generics.

    The class I was trying to test looked something like this:

    public class Response<T>{
        public final Map<String, String> map;
        public final T data;
    }
    

    However, the test was written like this:

    @Test
    public void testStuff() throws Exception {
        Map<String, String> map = new HashMap<>();
        Object data = new Object();
        Response target = new Response<>(map, data);
        assertThat(target.map, sameInstance(map));
        assertThat(target.data, sameInstance(data));
    }
    

    The compilation error is actually at the last line, because T is unknown (<?>) to the compiler, it couldn't find an appropriate matcher. I fixed the test by declaring the raw type.

    @Test
    public void testStuff() throws Exception {
        Map<String, String> map = new HashMap<>();
        Object data = new Object();
        Response<Object> target = new Response<>(map, data);
        assertThat(target.map, sameInstance(map));
        assertThat(target.data, sameInstance(data));
    }
    

    But I find it odd why the compiler complains about the previous line...