Равномерность карты с использованием Hamcrest
Я хотел бы использовать hamcrest, чтобы утверждать, что две карты равны, т.е. они имеют одинаковый набор ключей, указывающих на те же значения.
Мое последнее предположение:
assertThat( affA.entrySet(), hasItems( affB.entrySet() );
который дает:
Метод assertThat (T, Matcher) в типе Assert не применим для аргументов (Set > , Matcher → > )
Я также рассмотрел варианты containsAll и некоторые другие, предоставленные пакетами hamcrest. Может кто-то указать мне верное направление? Или мне нужно написать пользовательский макет?
Ответы
Ответ 1
Самый короткий способ, с которым я столкнулся, - это два утверждения:
assertThat( affA.entrySet(), everyItem(isIn(affB.entrySet())));
assertThat( affB.entrySet(), everyItem(isIn(affA.entrySet())));
Но вы, вероятно, также можете сделать:
assertThat(affA.entrySet(), equalTo(affB.entrySet()));
в зависимости от реализаций карт.
UPDATE: на самом деле существует один оператор, который работает независимо от типов коллекции:
assertThat(affA.entrySet, both(everyItem(isIn(affB.entrySet()))).and(containsInAnyOrder(affB.entrySet())));
Ответ 2
Иногда Map.equals()
достаточно. Но иногда вы не знаете, что типы Map
возвращаются кодом под тестами, поэтому вы не знаете, будет ли .equals()
правильно сравнивать карту неизвестного типа, возвращаемую кодом с построенной вами картой. Или вы не хотите связывать свой код с такими тестами.
Кроме того, построение карты отдельно для сравнения результата с ней является IMHO не очень элегантным:
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()
, который принимает сопоставления как параметры вместо точных значений ключа и значения. Это может быть полезно, если вам нужно что-то другое, кроме проверки равенства каждого ключа и значения.
Ответ 3
Я предпочитаю использовать Guava ImmutableMap. Они поддерживают Map.equals()
и их легко построить. Единственный трюк - явно указать параметры типа, так как hamcrest примет тип ImmutableMap
.
assertThat( actualValue,
Matchers.<Map<String, String>>equalTo( ImmutableMap.of(
"key1", "value",
"key2", "other-value"
) ) );
Ответ 4
Еще один доступный вариант - использовать расширение Cirneco для Hamcrest. Он имеет hasSameKeySet()
(а также другие матчи для коллекций Guava).
Согласно вашему примеру, это будет:
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>
Ответ 5
У Hamcrest теперь есть Matcher
для коллекции размера.
org.hamcrest.collection.IsCollectionWithSize
Ответ 6
Это работает как шарм и не требует двух утверждений, таких как принятый ответ.
assertThat( actualData.entrySet().toArray(),
arrayContainingInAnyOrder(expectedData.entrySet().toArray()) );
Ответ 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<K, V>
, вы можете добавить проверку ключей assertThat(results).containsOnlyKeys((K[]) expectations.keySet().toArray());