15

Let's say I have a Map:

Map<String,Object> map1 = new HashMap<String,Object>();
map1.put("foo1","foo1");
map1.put("foo2", Arrays.asList("foo2","bar2"));

Now I'd like to use Hamcrest matchers to verify the Map's values. If this were a Map< String,String > I would do something similar to this:

assertThat(map1, hasEntry("foo1", "foo1"));

However, I'm stuck when trying to use this with Map where the entries in the Map could be a String or a List of values. This works for the first entry:

assertThat(map1, hasEntry("foo1", (Object)"foo1"));

For the second entry I can't figure out how to setup the Matchers.

EDIT:

I also tried this, but it produces a compiler warning.

assertThat(
            map1,
            hasEntry(
                    "foo2",
                    contains(hasProperty("name", is("foo2")),
                            hasProperty("name", is("bar2")))));

"The method assertThat(T, Matcher) in the type Assert is not applicable for the arguments (Map, Matcher>>>)"

(The above was the solution here: Hamcrest compare collections )

Community
  • 1
  • 1
acvcu
  • 2,476
  • 10
  • 40
  • 56

2 Answers2

11

You cannot do this elegantly with Hamcrest hasEntry since it will do type checking when you try to use matchers over lists.

There is a feature request for this on https://github.com/hamcrest/JavaHamcrest/issues/388

The easiest option I think is to do something like this:

@Test
public void test() {
    Map<String, Object> map1 = new HashMap<>();
    map1.put("foo1", "foo1");
    map1.put("foo2", Arrays.asList("foo2", "bar2"));

    assertThat(map1, hasEntry("foo1", "foo1"));
    assertThat(map1, hasListEntry(is("foo2"), containsInAnyOrder("foo2", "bar2")));
}

@SuppressWarnings("unchecked")
public static org.hamcrest.Matcher<java.util.Map<String, Object>> hasListEntry(org.hamcrest.Matcher<String> keyMatcher, org.hamcrest.Matcher<java.lang.Iterable<?>> valueMatcher) {
    Matcher mapMatcher = org.hamcrest.collection.IsMapContaining.<String, List<?>>hasEntry(keyMatcher, valueMatcher);
    return mapMatcher;
}

hasListEntry is here only to prevent the compiler error. It does unchecked assignment that's why you need @SuppressWarnings("unchecked"). You can put this static method in your common test util for example.

Jarl
  • 2,831
  • 4
  • 24
  • 31
medvedev1088
  • 3,645
  • 24
  • 42
  • The only change I had to make was containsInAnyOrder((Object)"foo2",(Object) "bar2")... not sure why since Strings are Objects. – acvcu May 13 '15 at 18:04
  • @acvcu: It's because you're retrieving items from the map as `Object`, not `String`. Yes, they're still `String` instances, but they're being regarded as `Object` (analogous to `Object entry = "foo";`). – Makoto May 13 '15 at 19:06
1

Try in this way you can use ImmutableMap

 assertThat( actualValue,
            Matchers.<Map<String, Object>>equalTo( ImmutableMap.of(
                "key1", "value",
                "key2", "arrayrelated values"
) ) );

Hope it will work for you.

prashant thakre
  • 5,061
  • 3
  • 26
  • 39
  • The idea here is to use Hamcrest, and not to do it like this. While this approach would work *without* Hamcrest, I doubt that's what the OP is looking for. – Makoto May 13 '15 at 16:40
  • @Makoto I updated my answer which match the requirement as its based on Hemcrest Matcher – prashant thakre May 13 '15 at 16:46
  • There are multiple issues I found with this proposal. First you have to cast the values with (Object), and second I don't understand how you are trying to show the collection in the value for "key2"... – acvcu May 13 '15 at 17:00
  • This seems to work: assertThat(map1, Matchers.> equalTo(ImmutableMap.of("foo1", (Object) "foo1", "foo2", (Object) Arrays.asList("foo2","bar2")))); – acvcu May 13 '15 at 17:06
  • 1
    Any idea how I can get this to work with random ordering of the actual values returned? – acvcu May 13 '15 at 17:07
  • @acvcu I just given you code so that you can modify accordingly. – prashant thakre May 13 '15 at 17:08