How to use Hamcrest to inspect Map items
Asked Answered
N

4

33

I have been recently using Hamcrest library to write some tests and quite successful but now I need to do something more complex and started to see a lot of difficulties. I need to inpsect and verify the properties of the items in a Map. My production code looks something like this:

    Map<String, List<MyItem>> map = new HashMap<String, List<MyItem>>();
    map.put("one", Arrays.asList(new MyItem("One")));
    map.put("two",  Arrays.asList(new MyItem("Two")));
    map.put("three",  Arrays.asList(new MyItem("Three")));

I want to write some test codes like the following, but it doesn't compile. Looks like Hamcrest's hasEntry is type-parametered, while hasItem and hasProperty only expect Object.

    assertThat(map, Matchers.<String, List<MyItem>>hasEntry("one",  hasItem(hasProperty("name", is("One")))));

My IDE (Eclipse) is giving this error message: The parameterized method <String, List<HamcrestTest.MyItem>>hasEntry(String, List<HamcrestTest.MyItem>) of type Matchers is not applicable for the arguments (String, Matcher<Iterable<? super Object>>). For one thing I think Eclipse is confused of which hasEntry method I wanted to use, it should be hasEntry(org.hamcrest.Matcher<? super K> keyMatcher, org.hamcrest.Matcher<? super V> valueMatcher) , not the hasEntry(K key, V value).

Should I just give up and get the item from the Map and manually inspect each property? Is there a cleaner way?

Nesline answered 25/11, 2013 at 16:14 Comment(0)
C
45

Youu could just use contains or containsInAnyOrder. True, you'll have to list all items in the List that way, but it works cleaner than hasItem:

@SuppressWarnings("unchecked")
@Test
public void mapTest() {
  Map<String, List<MyItem>> map = new HashMap<String, List<MyItem>>();
  map.put("one", asList(new MyItem("1"), new MyItem("one")));

  assertThat(map, hasEntry(is("one"),
                           containsInAnyOrder(hasProperty("name", is("one")),
                                              hasProperty("name", is("1")))));
}
Conan answered 25/11, 2013 at 16:49 Comment(3)
I don't believe this will resolve the compile-time error because the hasEntry will return a Matcher<String, Iterable<Object>> instead of the required Matcher<String, List<MyItem>>Milinda
@JohnB: Believe it or not, but I tried it and it works just peachy for me.Conan
Should it work also for map with values of different type (Map<String, Object> to say it in java)? Because I'm trying it with mockito, and I have an Overload resolution failed compile error.Natalyanataniel
M
7

So just to make this simpler you might try this...

assertThat((Object)map, (Matcher)Matchers.hasEntry("one",  hasItem(hasProperty("name", is("One")))));

by going to a raw type you will get a warning but no compile error. If have used this trick in the past when I don't want to worry about getting all the casting just right for the compiler.

Also, you might consider using ItIterableContainingInOrder.containingInOrder(new MyItem("One"))). This will verify the entire list and if MyItem implements equals then you won't be using reflection in your tests.

Milinda answered 25/11, 2013 at 16:36 Comment(0)
P
7

Since @t0mppa didn't provide a good example on how to use Hamcrest's contains and containsInAnyOrder for this, here's a little something to get your started:

Map<Integer, String> columns = new HashMap<Integer, String>();
columns.put(1, "ID");
columns.put(2, "Title");
columns.put(3, "Description");

assertThat(columns.values(), contains("ID", "Title", "Description")); // passes
assertThat(columns.values(), contains("ID", "Description", "Title")); // fails
assertThat(columns.values(), containsInAnyOrder("ID", "Description", "Title")); // passes

Note that as opposed to hasItem and hasItems, these will only work if you provide them with a full list of all the values you'll be matching against. See Hamcrest's javadocs for more information.

Portulaca answered 18/6, 2016 at 2:24 Comment(2)
What is the import statements for contains?Burrussburry
@JeffCook: Almost every Hamcrest matcher can/should be imported via org.hamcrest.Matchers.<matcherName>.Portulaca
U
1

the hasEntry method has two signatures:

  • hasEntry(key, value)
  • hasEntry(matcher<key>, matcher<value>)

You are using the first signature, thus you are checking whether your map contains a matcher mapped to the string "one". t0mppa's answer is using the second signature, that's why it works. The good news is you don't need to list all the elements in the list, you can just

assertThat(map, hasEntry(is("one"), hasItem(hasProperty("name", is("One")))));
Umberto answered 1/6, 2021 at 8:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.