如何使用Hamcrest检查Map项目

时间:2013-11-25 16:14:27

标签: junit hamcrest

我最近使用Hamcrest库编写了一些测试并且相当成功,但现在我需要做一些更复杂的事情并开始看到很多困难。我需要inpsect并验证Map中项目的属性。我的生产代码看起来像这样:

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

我想编写一些测试代码,如下所示,但它不能编译。看起来Hamcrest的hasEntry是类型参数化的,而hasItem和hasProperty只期望Object。

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

我的IDE(Eclipse)给出了以下错误消息:<String, List<HamcrestTest.MyItem>>hasEntry(String, List<HamcrestTest.MyItem>)类型的参数化方法Matchers不适用于参数(String, Matcher<Iterable<? super Object>>)。首先,我认为Eclipse对于我想使用哪种hasEntry方法感到困惑,它应该是hasEntry(org.hamcrest.Matcher<? super K> keyMatcher, org.hamcrest.Matcher<? super V> valueMatcher),而不是hasEntry(K key, V value)

我应该放弃并从地图中获取项目并手动检查每个属性吗?有更清洁的方式吗?

4 个答案:

答案 0 :(得分:29)

Youu可以使用containscontainsInAnyOrder。是的,您必须以List的方式列出所有项目,但它比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")))));
}

答案 1 :(得分:5)

所以只是为了让这个更简单,你可以试试这个......

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

通过转到原始类型,您将收到警告但没有编译错误。如果过去我曾经使用过这个技巧,那么我不想担心所有的编译都适合编译器。

另外,您可以考虑使用ItIterableContainingInOrder.containingInOrder(new MyItem("One")))。这将验证整个列表,如果MyItem实现equals,那么您将不会在测试中使用反射。

答案 2 :(得分:4)

由于@ t0mppa没有为如何使用Hamcrest的containscontainsInAnyOrder提供一个很好的例子,所以这里有一些东西可以让你开始:

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

请注意,与hasItemhasItems相反,这些仅在您向他们提供您要匹配的所有值的完整列表时才有效。有关详细信息,请参阅Hamcrest的javadocs

答案 3 :(得分:0)

hasEntry 方法有两个签名:

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

您正在使用第一个签名,因此您正在检查您的地图是否包含映射到字符串“one”的匹配器。 t0mppa's answer 正在使用第二个签名,这就是它起作用的原因。好消息是你不需要列出列表中的所有元素,你可以

assertThat(map, hasEntry(is("one"), hasItem(hasProperty("name", is("One")))));