How to use Hamcrest to inspect Map items

33,325

Solution 1

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")))));
}

Solution 2

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.

Solution 3

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.

Share:
33,325
dnang
Author by

dnang

Updated on June 01, 2021

Comments

  • dnang
    dnang almost 3 years

    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?

  • John B
    John B over 10 years
    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>>
  • t0mppa
    t0mppa over 10 years
    @JohnB: Believe it or not, but I tried it and it works just peachy for me.
  • rascio
    rascio over 6 years
    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.