【问题标题】:How to use Hamcrest to inspect Map items如何使用 Hamcrest 检查地图项目
【发布时间】:2013-11-25 16:14:26
【问题描述】:

我最近一直在使用 Hamcrest 库编写一些测试并且非常成功,但现在我需要做一些更复杂的事情并且开始看到很多困难。我需要检查和验证地图中项目的属性。我的生产代码如下所示:

    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) 给出此错误消息:Matchers 类型的参数化方法 &lt;String, List&lt;HamcrestTest.MyItem&gt;&gt;hasEntry(String, List&lt;HamcrestTest.MyItem&gt;) 不适用于参数 (String, Matcher&lt;Iterable&lt;? super Object&gt;&gt;)。一方面,我认为 Eclipse 对我想使用的 hasEntry 方法感到困惑,它应该是 hasEntry(org.hamcrest.Matcher&lt;? super K&gt; keyMatcher, org.hamcrest.Matcher&lt;? super V&gt; valueMatcher) ,而不是 hasEntry(K key, V value)

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

【问题讨论】:

    标签: junit hamcrest


    【解决方案1】:

    你可以使用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")))));
    }
    

    【讨论】:

    • 我不相信这会解决编译时错误,因为hasEntry 将返回Matcher&lt;String, Iterable&lt;Object&gt;&gt; 而不是必需的Matcher&lt;String, List&lt;MyItem&gt;&gt;
    • @JohnB:信不信由你,但我试过了,对我来说效果很好。
    • 它是否也适用于具有不同类型值的地图(Map&lt;String, Object&gt; 在 java 中这么说)?因为我正在使用 mockito 进行尝试,我遇到了 Overload resolution failed 编译错误。
    【解决方案2】:

    所以为了简单起见,你可以试试这个......

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

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

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

    【讨论】:

      【解决方案3】:

      由于 @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

      【讨论】:

        【解决方案4】:

        hasEntry 方法有两个签名:

        • hasEntry(key, value)
        • hasEntry(matcher&lt;key&gt;, matcher&lt;value&gt;)

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-05
          相关资源
          最近更新 更多