Как выполнить более глубокое сопоставление ключей и значений с помощью assertj

Скажем, у меня есть класс вроде этого:

public class Character {
   public Character(String name){
      this.name = name;
   }
   private String name;
   public String getName() { return name; }
}

И позже карта

Map<Character, Integer> characterAges = new HashMap<Character, Integer>();
characterAges.put(new Character("Frodo"), 34);

Используя assertj, какой лучший способ проверить, что characterAges включает символ "Frodo"? Для возраста я могу сделать:

assertThat(characterAges).hasValue(34);

И я знаю, что мог:

assertThat(characterAges.keySet())
               .extracting("name")
               .contains("Frodo");

Но потом я теряю свою беглость. Что я действительно хочу, это что-то вроде этого:

assertThat(characterAges)
               .hasKey(key.extracting("name").contains("Frodo")
               .hasValue(34);

Или даже лучше, чтобы я мог убедиться, что мой ключ и значение соответствуют:

assertThat(characterAges)
               .hasEntry(key.extracting("name").contains("Frodo"), 34);

Возможно ли подобное?

Ответ 1

Для этого нет простого решения. Один из способов - реализовать custom Assertion для карты символов. Вот простой пример Assertion для этой проблемы:

public class CharacterMapAssert extends AbstractMapAssert<MapAssert<Character, Integer>, Map<Character, Integer>, Character, Integer> {

    public CharacterMapAssert(Map<Character, Integer> actual) {
        super(actual, CharacterMapAssert.class);
    }

    public static CharacterMapAssert assertThat(Map<Character, Integer> actual) {
        return new CharacterMapAssert(actual);
    }

    public CharacterMapAssert hasNameWithAge(String name, int age) {
        isNotNull();

        for (Map.Entry<Character, Integer> entrySet : actual.entrySet()) {
            if (entrySet.getKey().getName().contains(name) && (int) entrySet.getValue() == age) {
                return this;
            }
        }

        String msg = String.format("entry with name %s and age %s does not exist", name, age);
        throw new AssertionError(msg);
    }

}

В тестовом примере:

assertThat(characterAges).hasNameWithAge("Frodo", 34);

Помните, что для каждой пользовательской структуры данных вы можете написать собственное утверждение. Для класса Character вы можете сгенерировать утверждение с помощью AssertJ assertions generator.


Обновить Java 8

В Java 8 также может использоваться выражение Lambda Expression

    assertThat(characterAges).matches(
            (Map<Character, Integer> t)
            -> t.entrySet().stream().anyMatch((Map.Entry<Character, Integer> t1)
                    -> "Frodo".equals(t1.getKey().getName()) && 34 == t1.getValue()),
            "is Frodo and 34 years old"
    );

Ответ 3

Если вы не хотите идти по пути пользовательского утверждения и можете получить доступ к экземплярам символов в тестируемой системе (SUT) в тесте, другой вариант может быть

В ТУТ:

Character frodo = new Character("Frodo");
characterAges.put(frodo, 34);

И в тесте

MapEntry frodoAge34 = MapEntry.entry(frodo, 34);
assertThat(characterAges).contains(frodoAge34);

Ответ 4

Я не уверен насчет даты/версии введения, но в MapAssert есть куча утверждений. От http://joel-costigliola.github.io/assertj/core-8/api/org/assertj/core/api/MapAssert.html:

содержит (Map.Entry... записи) - Проверяет, что фактическая карта содержит данные записи в любом порядке.

containsAnyOf (Map.Entry... records) - Проверяет, что фактическая карта содержит хотя бы одну из указанных записей.

containsExactly (Map.Entry... records) - Проверяет, что фактическая карта содержит только данные записи и ничего больше по порядку.

containsKeys (KEY... keys) - Проверяет, что фактическая карта содержит данные ключи.

containsOnly (Map.Entry... records) - Проверяет, что фактическая карта содержит только данные записи и ничего больше в любом порядке.

containsOnlyKeys (KEY... keys) - Проверяет, что фактическая карта содержит только данные ключи и ничего больше, в любом порядке.

containsValues (VALUE... values) - Проверяет, что фактическая карта содержит заданные значения.

didNotContain (Map.Entry... records) - Проверяет, что фактическая карта не содержит заданные записи.

didNotContainKeys (KEY... keys) - Проверяет, что фактическая карта не содержит ни одного из заданных ключей.

extracting (Function, Object>... extractors) - использует данные функции для извлечения значений из тестируемого объекта в список, причем этот новый список становится тестируемым объектом.

Для вашего примера, метод containsExactly().

Ответ 5

Как насчет использования .entrySet() с .extracting()?

assertThat(characterAges.entrySet())
 .extracting(
       entry -> entry.getKey().getName(), 
       Map.Entry::getValue)
 .contains(tuple("Frodo", 34));