Як стверджувати, що Iterable містить елементи з певною властивістю?


103

Припустимо, я хочу спробувати тест методу з цим підписом:

List<MyItem> getMyItems();

Припустимо MyItem, це Pojo, який має багато властивостей, до одного з яких можна "name"отримати доступ getName().

Мені важливо переконатися в тому List<MyItem>, що або будь-який Iterableмістить два MyItemекземпляри, "name"властивості яких мають значення "foo"та "bar". Якщо будь-які інші властивості не відповідають, я не дуже цікавлюсь цілями цього тесту. Якщо назви відповідають, це успішний тест.

Я хотів би, щоб це був однолінійний, якщо це можливо. Ось якийсь "псевдосинтаксис" такого роду, що я хотів би зробити.

assert(listEntriesMatchInAnyOrder(myClass.getMyItems(), property("name"), new String[]{"foo", "bar"});

Чи вдасться Hamcrest для такого типу речей? Якщо так, то яка б була верхня версія мого псевдосинтаксису вище?

Відповіді:


125

Дякую @Razvan, який вказав на мене в правильному напрямку. Мені вдалося отримати його за один рядок, і я успішно погнав імпорт Hamcrest 1.3.

імпорт:

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;

код:

assertThat( myClass.getMyItems(), contains(
    hasProperty("name", is("foo")), 
    hasProperty("name", is("bar"))
));

49

Спробуйте:

assertThat(myClass.getMyItems(),
                          hasItem(hasProperty("YourProperty", is("YourValue"))));

2
як бічний вузол - це рішення для підкреслив (не assrtj)
Hartmut P.

46

Це не особливо Hamcrest, але я думаю, що тут варто згадати. Що я досить часто використовую в Java8, це щось на зразок:

assertTrue(myClass.getMyItems().stream().anyMatch(item -> "foo".equals(item.getName())));

(Відредаговано невеликим поліпшенням Родріго Манярі. Це трохи менш багатослівний. Див. Коментарі.)

Це може бути трохи складніше для читання, але мені подобається тип та безпека рефакторингу. Це також здорово для тестування кількох властивостей квасолі в поєднанні. наприклад, з виразом & java у фільтрі лямбда.


2
Невелике поліпшення: assertTrue (myClass.getMyItems (). Stream (). AnyMatch (item -> "foo" .equals (item.getName ()));
Rodrigo Manyari

@RodrigoManyari, відсутні дужки із закриттям
Абдулль

1
Це рішення втрачає можливість показати відповідне повідомлення про помилку.
Джуліо Каччін

@GiulioCaccin Я не думаю, що це робить. Якщо ви використовуєте JUnit, ви можете / повинні використовувати перевантажені методи твердження та написати assertTrue (..., "Моє власне повідомлення про помилку тесту"); Дивіться більше на junit.org/junit5/docs/current/api/org/junit/jupiter/api/…
Mario Eis

Я маю на увазі, якщо ви зробите твердження проти булевого, ви втрачаєте можливість автоматично друкувати фактичну / очікувану різницю. Можна стверджувати за допомогою відповідника, але для цього потрібно змінити цю відповідь, подібну до інших на цій сторінці.
Джуліо Каччін

20

Assertj в цьому хороший.

import static org.assertj.core.api.Assertions.assertThat;

    assertThat(myClass.getMyItems()).extracting("name").contains("foo", "bar");

Великий плюс для assertj порівняно з hamcrest - це легке використання коду.


16

AssertJ забезпечує чудову функцію в extracting(): ви можете передавати Functions для вилучення полів. Він забезпечує перевірку під час компіляції.
Ви також можете стверджувати розмір спочатку легко.

Це дало б:

import static org.assertj.core.api.Assertions;

Assertions.assertThat(myClass.getMyItems())
          .hasSize(2)
          .extracting(MyItem::getName)
          .containsExactlyInAnyOrder("foo", "bar"); 

containsExactlyInAnyOrder() стверджує, що список містить лише ці значення незалежно від порядку.

Щоб стверджувати, що список містить ці значення незалежно від порядку, але може містити й інші значення contains():

.contains("foo", "bar"); 

Як бічна примітка: щоб стверджувати кілька елементів з елементів a List, з AssertJ ми робимо це , вкладаючи очікувані значення для кожного елемента у tuple()функцію:

import static org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple;

Assertions.assertThat(myClass.getMyItems())
          .hasSize(2)
          .extracting(MyItem::getName, MyItem::getOtherValue)
          .containsExactlyInAnyOrder(
               tuple("foo", "OtherValueFoo"),
               tuple("bar", "OtherValueBar")
           ); 

4
Не розумійте, чому це не має жодних результатів. Я думаю, це найкраща відповідь, на сьогоднішній день.
PeMa

1
Бібліотека assertJ набагато читає, ніж API затвердження JUnit.
Сангімед

@Sangimed Погодився, а також я вважаю за краще, щоб він ховався.
davidxxx

На мою думку, це трохи менш читабельно, оскільки воно відокремлює "фактичне значення" від "очікуваного значення" та приводить їх у порядок, який повинен відповідати.
Терран

5

Поки ваш Список є конкретним класом, ви можете просто викликати метод містить () до тих пір, поки ви реалізували ваш метод equals () на MyItem.

// given 
// some input ... you to complete

// when
List<MyItems> results = service.getMyItems();

// then
assertTrue(results.contains(new MyItem("foo")));
assertTrue(results.contains(new MyItem("bar")));

Припускає, що ви реалізували конструктор, який приймає значення, на які потрібно стверджувати. Я усвідомлюю, що це не в одному рядку, але корисно знати, яке значення відсутнє, а не перевіряти обидва відразу.


1
Мені дуже подобається ваше рішення, але чи повинен він модифікувати весь цей код на тест?
Кевін Боуерсокс

Я думаю, що для кожної відповіді тут знадобиться певна установка тесту, виконання методу тестування, а потім затвердження властивостей. На мою відповідь немає нічого, що я бачу, лише те, що у мене є два твердження на сепаратних лініях, щоб невдале твердження могло чітко визначити, яке значення відсутнє.
Бред

Найкраще було б також включити повідомлення в assertTrue, щоб повідомлення про помилку було більш зрозумілим. Без повідомлення, якщо воно не вдасться, JUnit просто кине AssertionFailedError без жодного повідомлення про помилку. Тож краще включити щось на кшталт "результати повинні містити новий MyItem (\" foo \ ")".
Макс

Так, ти маєш рацію. Я б рекомендував Hamcrest в будь-якому випадку, і я ніколи не використовую assertTrue () сьогодні
Бред

Зі сторони, ваш POJO або DTO повинен визначити метод рівних
Tayab Hussain

1

AssertJ 3.9.1 підтримує пряме використання предиката в anyMatchметоді.

assertThat(collection).anyMatch(element -> element.someProperty.satisfiesSomeCondition())

Це, як правило, придатний випадок використання для довільно складного стану.

Для простих умов я віддаю перевагу використанню extractingметоду (див. Вище), оскільки отриманий ітерабельний під тест може підтримувати перевірку значення з кращою читабельністю. Приклад: він може надати спеціалізований API, такий як containsметод у відповіді Френка Неблунга. Або ви можете зателефонувати anyMatchна нього пізніше і скористатися посиланням на метод, наприклад "searchedvalue"::equals. Також в extractingметод можуть бути введені кілька екстракторів , результат згодом перевірений за допомогою tuple().

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.