0

Сравнение карт с помощью Hamcrest

15

Проблема с использованием Hamcrest для сравнения двух карт

Я хотел бы использовать 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. Можете ли вы указать мне правильное направление? Или мне нужно написать пользовательский матчер?

5 ответ(ов)

0

Самый короткий способ, который я придумал, состоит из двух утверждений:

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().toArray())));
0

Вопрос: Когда следует использовать Map.equals() для сравнения карт, а когда лучше подойти к этому более гибко?

Ответ:

В некоторых случаях Map.equals() вполне достаточно для проверки равенства карт. Однако бывает, что типы карт, возвращаемых кодом, который мы тестируем, неизвестны. В таких ситуациях может возникнуть вопрос, правильно ли 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), // проверяем, чтобы в карте не было лишних пар ключ/значение
                      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(), которая принимает матчеры в качестве параметров вместо конкретных значений ключа и значения. Это может быть полезно, если вам нужно проверить условия, отличные от простого тестирования на равенство для каждого ключа и значения.

0

Этот подход работает безупречно и не требует двух утверждений, как в принятом ответе.

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

В Hamcrest теперь есть Matcher для проверки размера коллекции — org.hamcrest.collection.IsCollectionWithSize.

Этот Matcher позволяет удобно проверять, содержит ли коллекция ожидаемое количество элементов. Например, вы можете использовать его следующим образом:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import java.util.List;

List<String> myList = List.of("one", "two", "three");
assertThat(myList, hasSize(3));

В этом примере производится проверка, что myList содержит ровно 3 элемента. Использование IsCollectionWithSize делает код читаемым и понятным.

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()))))
}

Таким образом, я смог обойти эту проблему с зарезервированным словом и успешно использовать новый синтаксис is(in(...)) в своих тестах.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь