«1. Обзор
Truth — это плавная и гибкая среда тестирования с открытым исходным кодом, предназначенная для того, чтобы сделать тестовые утверждения и сообщения об ошибках более читабельными.
В этой статье мы рассмотрим ключевые особенности платформы Truth и реализуем примеры, чтобы продемонстрировать ее возможности.
2. Зависимости Maven
Во-первых, нам нужно добавить правда и правда-java8-расширение в наш pom.xml:
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
<version>0.32</version>
</dependency>
<dependency>
<groupId>com.google.truth.extensions</groupId>
<artifactId>truth-java8-extension</artifactId>
<version>0.32</version>
<scope>test</scope>
</dependency>
Вы можете найти последние версии истины и истины-java8-расширения на Maven Central. 3. Введение. 8 — Необязательные и потоковые экземпляры. Guava — Необязательные объекты, Multimap, Multiset и Table. Пользовательские типы — путем расширения класса Subject, как мы увидим позже
Через классы Truth и Truth8 библиотека предоставляет служебные методы для написания утверждений, которые работают с субъектом, то есть с тестируемым значением или объектом.
Когда предмет известен, Truth может рассуждать во время компиляции о том, какие предложения известны для этого предмета. Это позволяет ему возвращать обертки вокруг нашего значения, которые объявляют методы предложения, специфичные для этого конкретного предмета.
-
Например, при проверке списка Truth возвращает экземпляр IterableSubject, определяющий такие методы, как contains() и containsAnyOf(), среди прочих. При утверждении на карте он возвращает MapSubject, который объявляет такие методы, как containsEntry() и containsKey().
4. Начало работы
Чтобы начать писать утверждения, давайте сначала импортируем точки входа Truth:
Теперь давайте напишем простой класс, который мы будем использовать в нескольких примерах, которые follow:
Обратите внимание на пользовательский метод equals(), в котором мы утверждаем, что два объекта User равны, если их имена равны.
5. Стандартные утверждения Java
import static com.google.common.truth.Truth.*;
import static com.google.common.truth.Truth8.*;
В этом разделе мы рассмотрим подробные примеры написания тестовых утверждений для стандартных типов Java.
public class User {
private String name = "John Doe";
private List<String> emails
= Arrays.asList("[email protected]", "[email protected]");
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
User other = (User) obj;
return Objects.equals(this.name, other.name);
}
// standard constructors, getters and setters
}
5.1. Утверждения объектов
Truth предоставляет оболочку Subject для выполнения утверждений об объектах. Субъект также является родителем всех других оболочек в библиотеке и объявляет методы для определения того, равен ли объект, в нашем случае пользователю, другому объекту:
или равен ли он данному объекту в списке :
или если это не так:
если это null или нет:
@Test
public void whenComparingUsers_thenEqual() {
User aUser = new User("John Doe");
User anotherUser = new User("John Doe");
assertThat(aUser).isEqualTo(anotherUser);
}
или если это экземпляр определенного класса:
@Test
public void whenComparingUser_thenInList() {
User aUser = new User();
assertThat(aUser).isIn(Arrays.asList(1, 3, aUser, null));
}
другие методы утверждения в классе Subject. Чтобы узнать их все, обратитесь к документации по предмету.
@Test
public void whenComparingUser_thenNotInList() {
// ...
assertThat(aUser).isNotIn(Arrays.asList(1, 3, "Three"));
}
В следующих разделах мы сосредоточимся на наиболее подходящих методах для каждого конкретного типа, поддерживаемого Truth. Однако имейте в виду, что все методы класса Subject также могут быть применены.
@Test
public void whenComparingUser_thenIsNull() {
User aUser = null;
assertThat(aUser).isNull();
}
@Test
public void whenComparingUser_thenNotNull() {
User aUser = new User();
assertThat(aUser).isNotNull();
}
5.2. Целочисленные, плавающие и двойные утверждения
@Test
public void whenComparingUser_thenInstanceOf() {
// ...
assertThat(aUser).isInstanceOf(User.class);
}
Целочисленные, плавающие и двойные экземпляры можно сравнивать на равенство:
если они больше:
или меньше:
Кроме того , Float и Double экземпляры также можно проверить, находятся ли они в пределах ожидаемой точности или нет:
@Test
public void whenComparingInteger_thenEqual() {
int anInt = 10;
assertThat(anInt).isEqualTo(10);
}
5.3. BigDecimal Assertions
@Test
public void whenComparingFloat_thenIsBigger() {
float aFloat = 10.0f;
assertThat(aFloat).isGreaterThan(1.0f);
}
Помимо общих утверждений, этот тип можно сравнивать без учета его масштаба:
@Test
public void whenComparingDouble_thenIsSmaller() {
double aDouble = 10.0f;
assertThat(aDouble).isLessThan(20.0);
}
5.4. Булевы утверждения
@Test
public void whenComparingDouble_thenWithinPrecision() {
double aDouble = 22.18;
assertThat(aDouble).isWithin(2).of(23d);
}
@Test
public void whenComparingFloat_thenNotWithinPrecision() {
float aFloat = 23.04f;
assertThat(aFloat).isNotWithin(1.3f).of(100f);
}
Предоставляются только два соответствующих метода, isTrue() и isFalse():
5.5. Утверждения строки
@Test
public void whenComparingBigDecimal_thenEqualIgnoringScale() {
BigDecimal aBigDecimal = BigDecimal.valueOf(1000, 3);
assertThat(aBigDecimal).isEqualToIgnoringScale(new BigDecimal(1.0));
}
Мы можем проверить, начинается ли строка с определенного текста:
Кроме того, мы можем проверить, содержит ли строка заданную строку, заканчивается ли она ожидаемым значением или пуста. Тестовые примеры для этих и других методов доступны в исходном коде.
@Test
public void whenCheckingBoolean_thenTrue() {
boolean aBoolean = true;
assertThat(aBoolean).isTrue();
}
5.6. Утверждения массива
Мы можем проверить массивы, чтобы увидеть, равны ли они другим массивам:
@Test
public void whenCheckingString_thenStartsWith() {
String aString = "This is a string";
assertThat(aString).startsWith("This");
}
или пусты ли они:
5.7. Comparable Asserts
Помимо проверки того, является ли Comparable большим или меньшим, чем другой экземпляр, мы можем проверить, являются ли они по крайней мере заданным значением:
@Test
public void whenComparingArrays_thenEqual() {
String[] firstArrayOfStrings = { "one", "two", "three" };
String[] secondArrayOfStrings = { "one", "two", "three" };
assertThat(firstArrayOfStrings).isEqualTo(secondArrayOfStrings);
}
Кроме того, мы можем проверить, находятся ли они в пределах определенного диапазон:
@Test
public void whenCheckingArray_thenEmpty() {
Object[] anArray = {};
assertThat(anArray).isEmpty();
}
или в определенном списке:
«
@Test
public void whenCheckingComparable_thenAtLeast() {
Comparable<Integer> aComparable = 5;
assertThat(aComparable).isAtLeast(1);
}
«Мы также можем проверить эквивалентность двух экземпляров Comparable в соответствии с методом compareTo() класса.
@Test
public void whenCheckingComparable_thenInRange() {
// ...
assertThat(aComparable).isIn(Range.closed(1, 10));
}
Во-первых, давайте изменим наш класс User, чтобы реализовать интерфейс Comparable:
@Test
public void whenCheckingComparable_thenInList() {
// ...
assertThat(aComparable).isIn(Arrays.asList(4, 5, 6));
}
Теперь давайте утверждаем, что два пользователя с одинаковым именем эквивалентны:
5.8. Утверждения Iterable
public class User implements Comparable<User> {
// ...
public int compareTo(User o) {
return this.getName().compareToIgnoreCase(o.getName());
}
}
В дополнение к утверждению размера экземпляра Iterable, независимо от того, является ли он пустым или не имеет дубликатов, наиболее типичными утверждениями для Iterable являются утверждения о том, что он содержит некоторый элемент:
@Test
public void whenComparingUsers_thenEquivalent() {
User aUser = new User();
aUser.setName("John Doe");
User anotherUser = new User();
anotherUser.setName("john doe");
assertThat(aUser).isEquivalentAccordingToCompareTo(anotherUser);
}
что он содержит любой элемент другого Итерируемый:
и что субъект имеет те же элементы в том же порядке, что и другой:
@Test
public void whenCheckingIterable_thenContains() {
List<Integer> aList = Arrays.asList(4, 5, 6);
assertThat(aList).contains(5);
}
и если он упорядочен с использованием пользовательского компаратора:
@Test
public void whenCheckingIterable_thenContainsAnyInList() {
List<Integer> aList = Arrays.asList(1, 2, 3);
assertThat(aList).containsAnyIn(Arrays.asList(1, 5, 10));
}
5.9. Утверждения карты
@Test
public void whenCheckingIterable_thenContainsExactElements() {
List<String> aList = Arrays.asList("10", "20", "30");
List<String> anotherList = Arrays.asList("10", "20", "30");
assertThat(aList)
.containsExactlyElementsIn(anotherList)
.inOrder();
}
В дополнение к утверждению, что экземпляр карты пуст или нет, или имеет определенный размер; мы можем проверить, есть ли у него определенная запись:
@Test
public void givenComparator_whenCheckingIterable_thenOrdered() {
Comparator<String> aComparator
= (a, b) -> new Float(a).compareTo(new Float(b));
List<String> aList = Arrays.asList("1", "012", "0020", "100");
assertThat(aList).isOrdered(aComparator);
}
есть ли у него определенный ключ:
или есть ли у него те же записи, что и у другой карты:
@Test
public void whenCheckingMap_thenContainsEntry() {
Map<String, Object> aMap = new HashMap<>();
aMap.put("one", 1L);
assertThat(aMap).containsEntry("one", 1L);
}
5.10. Exception Assertions
@Test
public void whenCheckingMap_thenContainsKey() {
// ...
assertThat(map).containsKey("one");
}
Для объектов Exception предоставляются только два важных метода.
@Test
public void whenCheckingMap_thenContainsEntries() {
Map<String, Object> aMap = new HashMap<>();
aMap.put("first", 1L);
aMap.put("second", 2.0);
aMap.put("third", 3f);
Map<String, Object> anotherMap = new HashMap<>(aMap);
assertThat(aMap).containsExactlyEntriesIn(anotherMap);
}
Мы можем написать утверждения, адресованные причине исключения:
или его сообщению:
5.11. Утверждения класса
@Test
public void whenCheckingException_thenInstanceOf() {
Exception anException
= new IllegalArgumentException(new NumberFormatException());
assertThat(anException)
.hasCauseThat()
.isInstanceOf(NumberFormatException.class);
}
Существует только один важный метод для утверждений класса, с помощью которого мы можем проверить, может ли класс быть назначен другому:
@Test
public void whenCheckingException_thenCauseMessageIsKnown() {
Exception anException
= new IllegalArgumentException("Bad value");
assertThat(anException)
.hasMessageThat()
.startsWith("Bad");
}
6. Утверждения Java 8
Необязательный и Stream — единственные два типа Java 8. что Истина поддерживает.
@Test
public void whenCheckingClass_thenIsAssignable() {
Class<Double> aClass = Double.class;
assertThat(aClass).isAssignableTo(Number.class);
}
6.1. Необязательные утверждения
Существует три важных метода проверки необязательных утверждений.
Мы можем проверить, имеет ли он конкретное значение:
если значение присутствует:
или если значение отсутствует:
@Test
public void whenCheckingJavaOptional_thenHasValue() {
Optional<Integer> anOptional = Optional.of(1);
assertThat(anOptional).hasValue(1);
}
6.2. Утверждения потока
@Test
public void whenCheckingJavaOptional_thenPresent() {
Optional<String> anOptional = Optional.of("Baeldung");
assertThat(anOptional).isPresent();
}
Утверждения для потока очень похожи на утверждения для итерируемого объекта.
@Test
public void whenCheckingJavaOptional_thenEmpty() {
Optional anOptional = Optional.empty();
assertThat(anOptional).isEmpty();
}
Например, мы можем проверить, содержит ли конкретный поток все объекты Iterable в том же порядке:
Дополнительные примеры см. в разделе Утверждения Iterable.
7. Утверждения Guava
@Test
public void whenCheckingStream_thenContainsInOrder() {
Stream<Integer> anStream = Stream.of(1, 2, 3);
assertThat(anStream)
.containsAllOf(1, 2, 3)
.inOrder();
}
В этом разделе мы увидим примеры утверждений для поддерживаемых типов Guava в Truth.
7.1. Необязательные утверждения
Существуют также три важных метода утверждений для опционального объекта Guava. Методы hasValue() и isPresent() ведут себя точно так же, как с необязательным Java 8.
Но вместо isEmpty(), чтобы утверждать, что необязательный объект отсутствует, мы используем isAbsent():
7.2. Утверждения Multimap
Multimap и стандартные утверждения Map очень похожи.
@Test
public void whenCheckingGuavaOptional_thenIsAbsent() {
Optional anOptional = Optional.absent();
assertThat(anOptional).isAbsent();
}
Одно заметное отличие состоит в том, что мы можем получить несколько значений ключа в Multimap и сделать утверждения для этих значений.
Вот пример, который проверяет, имеют ли значения ключа one размер, равный двум:
Дополнительные примеры см. в разделе Утверждения карты.
7.3. Утверждения Multiset
@Test
public void whenCheckingGuavaMultimap_thenExpectedSize() {
Multimap<String, Object> aMultimap = ArrayListMultimap.create();
aMultimap.put("one", 1L);
aMultimap.put("one", 2.0);
assertThat(aMultimap)
.valuesForKey("one")
.hasSize(2);
}
Утверждения для объектов Multiset включают утверждения для объекта Iterable и один дополнительный метод для проверки того, имеет ли ключ определенное количество вхождений:
7.4. Утверждения таблицы
Помимо проверки ее размера или того, где она пуста, мы можем проверить таблицу, чтобы проверить, содержит ли она конкретное сопоставление для данной строки и столбца:
@Test
public void whenCheckingGuavaMultiset_thenExpectedCount() {
TreeMultiset<String> aMultiset = TreeMultiset.create();
aMultiset.add("baeldung", 10);
assertThat(aMultiset).hasCount("baeldung", 10);
}
или содержит ли она определенную ячейку: ~~ ~
Кроме того, мы можем проверить, содержит ли он заданную строку, столбец или значение. См. исходный код для соответствующих тестовых примеров.
@Test
public void whenCheckingGuavaTable_thenContains() {
Table<String, String, String> aTable = TreeBasedTable.create();
aTable.put("firstRow", "firstColumn", "baeldung");
assertThat(aTable).contains("firstRow", "firstColumn");
}
8. Пользовательские сообщения об ошибках и метки
@Test
public void whenCheckingGuavaTable_thenContainsCell() {
Table<String, String, String> aTable = getDummyGuavaTable();
assertThat(aTable).containsCell("firstRow", "firstColumn", "baeldung");
}
Когда утверждение терпит неудачу, Truth отображает очень читаемые сообщения, указывающие, что именно пошло не так. Однако иногда необходимо добавить к этим сообщениям дополнительную информацию, чтобы предоставить более подробную информацию о том, что произошло.
Truth позволяет нам настраивать эти сообщения об ошибках:
После запуска теста мы получаем следующий вывод:
Кроме того, мы можем добавить пользовательскую метку, которая будет отображаться перед нашей темой в Сообщения об ошибках. Это может пригодиться, когда объект не имеет полезного строкового представления:
@Test
public void whenFailingAssertion_thenCustomMessage() {
assertWithMessage("TEST-985: Secret user subject was NOT null!")
.that(new User())
.isNull();
}
Если мы запустим тест, мы увидим следующий вывод:
TEST-985: Secret user subject was NOT null!:
Not true that <[email protected]> is null
9. Расширения
@Test
public void whenFailingAssertion_thenMessagePrefix() {
User aUser = new User();
assertThat(aUser)
.named("User [%s]", aUser.getName())
.isNull();
}
Расширение истины означает мы можем добавить поддержку пользовательских типов. Для этого нам нужно создать класс, который:
Not true that User [John Doe]
(<[email protected]>) is null
«расширяет класс Subject или один из его подклассов определяет конструктор, который принимает два аргумента — FailureStrategy и экземпляр нашего пользовательского типа объявляет поле типа SubjectFactory, которое Truth будет использовать для создания экземпляров нашего пользовательского субъекта реализует статический assertThat () метод, который принимает наш пользовательский тип, предоставляет наш API проверки утверждений
Теперь, когда мы знаем, как расширить Truth, давайте создадим класс, который добавляет поддержку объектов типа User:
-
Теперь мы можем статически импортировать метод assertThat() нашего пользовательского объекта и напишите несколько тестов:
10. Заключение
public class UserSubject
extends ComparableSubject<UserSubject, User> {
private UserSubject(
FailureStrategy failureStrategy, User target) {
super(failureStrategy, target);
}
private static final
SubjectFactory<UserSubject, User> USER_SUBJECT_FACTORY
= new SubjectFactory<UserSubject, User>() {
public UserSubject getSubject(
FailureStrategy failureStrategy, User target) {
return new UserSubject(failureStrategy, target);
}
};
public static UserSubject assertThat(User user) {
return Truth.assertAbout(USER_SUBJECT_FACTORY).that(user);
}
public void hasName(String name) {
if (!actual().getName().equals(name)) {
fail("has name", name);
}
}
public void hasNameIgnoringCase(String name) {
if (!actual().getName().equalsIgnoreCase(name)) {
fail("has name ignoring case", name);
}
}
public IterableSubject emails() {
return Truth.assertThat(actual().getEmails());
}
}
В этом уроке мы исследовали возможности, которые Truth дает нам для написания более читаемых тестов и сообщений об ошибках.
@Test
public void whenCheckingUser_thenHasName() {
User aUser = new User();
assertThat(aUser).hasName("John Doe");
}
@Test
public void whenCheckingUser_thenHasNameIgnoringCase() {
// ...
assertThat(aUser).hasNameIgnoringCase("john doe");
}
@Test
public void givenUser_whenCheckingEmails_thenExpectedSize() {
// ...
assertThat(aUser)
.emails()
.hasSize(2);
}
Мы продемонстрировали самые популярные методы утверждения для поддерживаемых типов Java и Guava, настраиваемые сообщения об ошибках и расширенную истину с настраиваемыми темами.
Как всегда, полный исходный код этой статьи можно найти на Github.
«
As always, complete source code for this article can be found over on Github.