【问题标题】:Map equality using Hamcrest使用 Hamcrest 映射相等
【发布时间】:2010-03-24 16:08:20
【问题描述】:

我想使用 hamcrest 来断言两个映射是相等的,即它们具有指向相同值的相同键集。

我目前的最佳猜测是:

assertThat( affA.entrySet(), hasItems( affB.entrySet() );

给出:

Assert 类型中的方法 assertThat(T, Matcher<T>) 不适用于参数 (Set<Map.Entry<Householdtypes,Double>>, Matcher<Iterable<Set<Map.Entry<Householdtypes,Double>>>>)

我还研究了 containsAll 的变体,以及 hamcrest 软件包提供的其他一些变体。谁能指出我正确的方向?还是我必须编写一个自定义匹配器?

【问题讨论】:

  • 我也试过containsAll等。前段时间它似乎没有用 - 显然hamcrest有点不可靠:-(
  • Map 实现的.equals() 不能使用有什么原因吗?
  • 啊 - 我没有意识到集合可以进行正确的 .equals() 比较。一直都是这样吗?这让生活更轻松!谢谢!

标签: java collections hamcrest


【解决方案1】:

我想出的最短方法是两种说法:

assertThat( affA.entrySet(), everyItem(isIn(affB.entrySet())));
assertThat( affB.entrySet(), everyItem(isIn(affA.entrySet())));

但你也可以这样做:

assertThat(affA.entrySet(), equalTo(affB.entrySet()));

取决于地图的实现,并牺牲差异报告的清晰度:这只会告诉您存在差异,而上面的陈述也会告诉您是哪一个。

更新:实际上有一个语句独立于集合类型:

assertThat(affA.entrySet(), both(everyItem(isIn(affB.entrySet()))).and(containsInAnyOrder(affB.entrySet())));

【讨论】:

  • @Taras:报告对.equals()的帮助不大
  • 我必须将对 affB 的第二次访问转换为数组:.and(containsInAnyOrder(affB.entrySet().toArray())
  • entrySet 中缺少的() 和必要的toArray() 结合起来,最终得到:assertThat(affA.entrySet(), both(everyItem(isIn(affB.entrySet()))).and(containsInAnyOrder(affB.entrySet().toArray())))
【解决方案2】:

有时Map.equals() 就足够了。但有时您不知道被测试代码返回的Maps 的类型,因此您不知道.equals() 是否会正确地将代码返回的未知类型的映射与您构建的映射进行比较。或者您不想将代码与此类测试绑定。

另外,单独构建一个地图来比较结果是恕我直言,不是很优雅:

Map<MyKey, MyValue> actual = methodUnderTest();

Map<MyKey, MyValue> expected = new HashMap<MyKey, MyValue>();
expected.put(new MyKey(1), new MyValue(10));
expected.put(new MyKey(2), new MyValue(20));
expected.put(new MyKey(3), new MyValue(30));
assertThat(actual, equalTo(expected));

我更喜欢使用机器:

import static org.hamcrest.Matchers.hasEntry;

Map<MyKey, MyValue> actual = methodUnderTest();
assertThat(actual, allOf(
                      hasSize(3), // make sure there are no extra key/value pairs in map
                      hasEntry(new MyKey(1), new MyValue(10)),
                      hasEntry(new MyKey(2), new MyValue(20)),
                      hasEntry(new MyKey(3), new MyValue(30))
));

我必须自己定义hasSize()

public static <K, V> Matcher<Map<K, V>> hasSize(final int size) {
    return new TypeSafeMatcher<Map<K, V>>() {
        @Override
        public boolean matchesSafely(Map<K, V> kvMap) {
            return kvMap.size() == size;
        }

        @Override
        public void describeTo(Description description) {
            description.appendText(" has ").appendValue(size).appendText(" key/value pairs");
        }
    };
}

hasEntry() 的另一个变体将匹配器作为参数,而不是键和值的精确值。如果您需要对每个键和值进行相等性测试以外的其他东西,这可能会很有用。

【讨论】:

  • 不要使用你自己的hasSize方法,你应该使用org.hamcrest.collection.IsMapWithSize.aMapWithSize(Matcher&lt;? super Integer&gt;)
【解决方案3】:

我喜欢使用Guava ImmutableMap。它们支持Map.equals() 并且易于构建。唯一的技巧是显式指定类型参数,因为 hamcrest 将假定 ImmutableMap 类型。

assertThat( actualValue,
            Matchers.<Map<String, String>>equalTo( ImmutableMap.of(
                "key1", "value",
                "key2", "other-value"
) ) );

【讨论】:

    【解决方案4】:

    现在可用的另一个选项是将 Cirneco extension 用于 Hamcrest。它有hasSameKeySet()(以及其他用于番石榴“集合”的匹配器)。 根据您的示例,它将是:

    assertThat(affA, hasSameKeySet(affB));
    

    您可以对基于 JDK7 的项目使用以下依赖项:

    <dependency>
      <groupId>it.ozimov</groupId>
      <artifactId>java7-hamcrest-matchers</artifactId>
      <version>0.7.0</version>
    </dependency>
    

    如果您使用的是 JDK8 或更高版本,则如下:

    <dependency>
      <groupId>it.ozimov</groupId>
      <artifactId>java8-hamcrest-matchers</artifactId>
      <version>0.7.0</version>
    </dependency>
    

    【讨论】:

    • 那不是只比较键而不比较值吗?这个问题也想比较值。
    • @Dr.Hans-PeterStörr 不,问题显示条目集进行了比较
    【解决方案5】:

    这就像一个魅力,不需要像接受的答案那样的两个断言。

    assertThat( actualData.entrySet().toArray(), 
        arrayContainingInAnyOrder(expectedData.entrySet().toArray()) );
    

    【讨论】:

      【解决方案6】:

      Hamcrest 现在有一个 Matcher 用于收集尺寸。

      org.hamcrest.collection.IsCollectionWithSize

      【讨论】:

      • 那没有回答原来的问题。
      【解决方案7】:

      如果您需要将一组结果与预期进行比较,并且如果您选择使用assertj 库,您可以这样做:

      // put set of expected values by your test keys
      Map<K, V> expectations = ...;
      
      // for each test key get result
      Map<K, V> results = expectations.keySet().stream().collect(toMap(k -> k, k -> getYourProductionResult(k)));
      
      assertThat(results).containsAllEntriesOf(expectations);
      

      请注意,containsAllEntriesOf 不会比较地图是否相等。如果您的生产代码实际上返回 Map&lt;K, V&gt;,您可能需要添加对密钥的检查 assertThat(results).containsOnlyKeys((K[]) expectations.keySet().toArray());

      【讨论】:

        【解决方案8】:

        一种非常简单的方法是使用 Guava 的 com.google.common.collect.Maps 类中的实用方法。

        assertThat(Maps.difference(map1,map2).areEqual(),is(true));
        

        【讨论】:

        • 然而,这消除了描述性断言失败,这是使用 Hamcrest 的主要原因之一。
        【解决方案9】:

        我使用 groovy 和 org.hamcrest:hamcrest:2.2 进行了这个线程测试

        不推荐使用isIn 方法,建议改用is(in(...))

        但是in 是 groovy 中的关键字!

        所以我最终将导入别名化为:

        import static org.hamcrest.Matchers.*
        import static org.hamcrest.Matchers.in as matchIn
        ....
        ....
        @Test
        void myTestMethod() {
            Map expectedSubMap = [
                    one: "One",
                    three: "Three"
                    ]
            Map result = getMapToTest()
            assertThat(expectedSubMap.entrySet(), everyItem(is(matchIn(result.entrySet()))))
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-25
          • 1970-01-01
          • 1970-01-01
          • 2012-03-03
          • 2015-06-22
          • 1970-01-01
          相关资源
          最近更新 更多