55

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

我目前最好的猜测是:

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

这使:

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

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

4

9 回答 9

59

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

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())));
于 2012-08-27T15:45:10.317 回答
40

有时Map.equals()就足够了。但是有时您不知道Map被测试代码返回的 s 的类型,因此您不知道是否.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()将匹配器作为参数,而不是键和值的精确值。如果您需要对每个键和值进行相等性测试以外的东西,这可能很有用。

于 2011-11-19T19:50:07.477 回答
4

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

assertThat( actualValue,
            Matchers.<Map<String, String>>equalTo( ImmutableMap.of(
                "key1", "value",
                "key2", "other-value"
) ) );
于 2015-04-16T16:10:26.267 回答
2

现在可用的另一个选项是为 Hamcrest 使用Cirneco 扩展。它有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>
于 2015-12-30T14:17:57.140 回答
2

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

assertThat( actualData.entrySet().toArray(), 
    arrayContainingInAnyOrder(expectedData.entrySet().toArray()) );
于 2017-06-20T17:13:12.663 回答
0

Hamcrest 现在有一个Matcher适合大小的集合。

org.hamcrest.collection.IsCollectionWithSize

于 2016-09-10T06:51:39.637 回答
0

如果您需要将一组结果与预期进行比较,并且如果您选择使用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不比较地图是否相等。如果您的生产代码实际上返回 aMap<K, V>您可能需要添加密钥检查assertThat(results).containsOnlyKeys((K[]) expectations.keySet().toArray());

于 2017-08-28T14:00:58.433 回答
0

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

assertThat(Maps.difference(map1,map2).areEqual(),is(true));
于 2018-05-03T06:43:04.723 回答
0

我用 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()))))
}
于 2021-06-11T11:59:34.277 回答