1. Overview
1.概述
Truth is a fluent and flexible open-source testing framework designed to make test assertions and failure messages more readable.
Truth是一个流畅和灵活的开源测试框架,旨在使测试断言和失败信息更具可读性。。
In this article, we’ll explore the key features of the Truth framework and implement examples to showcase its capabilities.
在这篇文章中,我们将探讨Truth框架的主要特征,并实施实例来展示其功能。
2. Maven Dependencies
2.Maven的依赖性
First, we need to add the truth and truth-java8-extension to our pom.xml:
首先,我们需要将truth和truth-java8-extension添加到我们的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>
You can find the latest versions of truth and truth-java8-extension on Maven Central.
您可以在Maven中心找到truth和truth-java8-extension的最新版本。
3. Introduction
3.简介
Truth allows us to write readable assertions and failure messages for a variety of classes:
Truth允许我们为各种类编写可读的断言和失败信息。
- Standard Java – primitives, arrays, strings, objects, collections, throwables, classes, etc.
- Java 8 – Optional and Stream instances
- Guava – Optional, Multimap, Multiset, and Table objects
- Custom types – by extending the Subject class, as we’ll see later
Through the Truth and Truth8 classes, the library provides utility methods for writing assertions that work on a subject, that’s the value or object under test.
通过Truth和Truth8类,该库提供了编写断言的实用方法,这些断言对subject起作用,也就是被测试的值或对象。
Once the subject is known, Truth can reason at compile time about what propositions are known for that subject. This allows it to return wrappers around our value that declare proposition methods specific to that particular subject.
一旦知道了主题,Truth就可以在编译时推理出该主题的已知命题。这使得它能够返回围绕我们的值的包装器,这些包装器声明了特定于该主题的命题方法。
For example, when asserting on a list, Truth returns an IterableSubject instance defining methods like contains() and containsAnyOf(), among others. When asserting on a Map, it returns a MapSubject that declares methods like containsEntry() and containsKey().
例如,当对一个列表进行断言时,Truth返回一个IterableSubject实例,定义了contains()和containsAnyOf()等方法。当对Map进行断言时,它返回一个MapSubject,声明了containsEntry()和containsKey()等方法。
4. Getting Started
4.入门
To start writing assertions, let’s first import Truth‘s entry points:
为了开始编写断言,让我们首先导入Truth的入口点。
import static com.google.common.truth.Truth.*;
import static com.google.common.truth.Truth8.*;
Now, let’s write a simple class that we’ll use in a few of the examples that follow:
现在,让我们写一个简单的类,我们将在接下来的几个例子中使用。
public class User {
private String name = "John Doe";
private List<String> emails
= Arrays.asList("contact@baeldung.com", "staff@baeldung.com");
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
}
Notice the custom equals() method, in which we state that two User objects are equal if their names are.
请注意自定义的equals()方法,在该方法中我们说明,如果两个User对象的名字相同,那么它们就是相等的。
5. Standard Java Assertions
5.标准Java断言
In this section, we’ll see detailed examples of how to write test assertions for standard Java types.
在本节中,我们将看到如何为标准Java类型编写测试断言的详细例子。
5.1. Object Assertions
5.1.对象断言
Truth provides the Subject wrapper for performing assertions on objects. Subject is also the parent of all other wrappers in the library and declares methods for determining if an Object, in our case a User, is equal to another object:
Truth提供了Subject包装器,用于对对象执行断言。Subject也是库中所有其它包装器的父类,它声明了确定一个Object(在我们的例子中是User)是否等于另一个对象的方法。
@Test
public void whenComparingUsers_thenEqual() {
User aUser = new User("John Doe");
User anotherUser = new User("John Doe");
assertThat(aUser).isEqualTo(anotherUser);
}
or if it’s equal to a given object in a list:
或者如果它等于一个列表中的某个对象。
@Test
public void whenComparingUser_thenInList() {
User aUser = new User();
assertThat(aUser).isIn(Arrays.asList(1, 3, aUser, null));
}
or if it isn’t:
或者如果不是这样。
@Test
public void whenComparingUser_thenNotInList() {
// ...
assertThat(aUser).isNotIn(Arrays.asList(1, 3, "Three"));
}
if it’s null or not:
如果它是空的,或不是。
@Test
public void whenComparingUser_thenIsNull() {
User aUser = null;
assertThat(aUser).isNull();
}
@Test
public void whenComparingUser_thenNotNull() {
User aUser = new User();
assertThat(aUser).isNotNull();
}
or if it’s an instance of a particular class:
或者说,如果它是一个特定类的实例。
@Test
public void whenComparingUser_thenInstanceOf() {
// ...
assertThat(aUser).isInstanceOf(User.class);
}
There are other assertion methods in the Subject class. To discover them all, refer to the Subject documentation.
Subject类中还有其他断言方法。要发现它们,请参考Subject 文档。
In the following sections, we are going to focus on the most relevant methods for each particular type Truth supports. However, keep in mind that all methods in the Subject class can also be applied.
在下面的章节中,我们将集中讨论每个特定类型Truth支持的最相关的方法。然而,请记住,Subject类中的所有方法也可以应用。
5.2. Integer, Float, and Double Assertions
5.2.Integer, Float, and Double断言
Integer, Float, and Double instances can be compared for equality:
Integer, Float, 和 Double 实例可以被比较为相等。
@Test
public void whenComparingInteger_thenEqual() {
int anInt = 10;
assertThat(anInt).isEqualTo(10);
}
if they are bigger:
如果他们更大的话。
@Test
public void whenComparingFloat_thenIsBigger() {
float aFloat = 10.0f;
assertThat(aFloat).isGreaterThan(1.0f);
}
or smaller:
或更小。
@Test
public void whenComparingDouble_thenIsSmaller() {
double aDouble = 10.0f;
assertThat(aDouble).isLessThan(20.0);
}
Furthermore, Float and Double instances can also be checked to see if they are within an expected precision or not:
此外,Float和Double实例也可以被检查,看它们是否在一个预期的精度内。
@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);
}
5.3. BigDecimal Assertions
5.3.BigDecimal断言
Besides the common assertions, this type can be compared ignoring its scale:
除了常见的断言,这种类型的比较可以忽略其规模。
@Test
public void whenComparingBigDecimal_thenEqualIgnoringScale() {
BigDecimal aBigDecimal = BigDecimal.valueOf(1000, 3);
assertThat(aBigDecimal).isEqualToIgnoringScale(new BigDecimal(1.0));
}
5.4. Boolean Assertions
5.4.布尔断言
Only two relevant methods are provided, isTrue() and isFalse():
只提供了两个相关方法,isTrue()和isFalse()。
@Test
public void whenCheckingBoolean_thenTrue() {
boolean aBoolean = true;
assertThat(aBoolean).isTrue();
}
5.5. String Assertions
5.5.字符串断言
We can test whether a String starts with a particular text:
我们可以测试一个String是否以一个特定的文本开始。
@Test
public void whenCheckingString_thenStartsWith() {
String aString = "This is a string";
assertThat(aString).startsWith("This");
}
In addition, we can check if the string contains a given String, if it ends with an expected value or whether it’s empty. Test cases for these and other methods are available in the source code.
此外,我们还可以检查字符串是否包含给定的String,是否以预期值结束,或者是否为空。这些方法和其他方法的测试案例可在源代码中找到。
5.6. Array Assertions
5.6.阵列断言
We can check Arrays to see if they are equal to other arrays:
我们可以检查Arrays,看看它们是否与其他数组相等。
@Test
public void whenComparingArrays_thenEqual() {
String[] firstArrayOfStrings = { "one", "two", "three" };
String[] secondArrayOfStrings = { "one", "two", "three" };
assertThat(firstArrayOfStrings).isEqualTo(secondArrayOfStrings);
}
or if they are empty:
或者如果它们是空的。
@Test
public void whenCheckingArray_thenEmpty() {
Object[] anArray = {};
assertThat(anArray).isEmpty();
}
5.7. Comparable Assertions
5.7.可比较的断言
Besides testing whether a Comparable is greater than or less than another instance, we can check to see if they are at least a given value:
除了测试一个可比性是否大于或小于另一个实例外,我们还可以检查它们是否至少是一个给定值。
@Test
public void whenCheckingComparable_thenAtLeast() {
Comparable<Integer> aComparable = 5;
assertThat(aComparable).isAtLeast(1);
}
Also, we can test whether they are within a particular range:
同时,我们可以测试它们是否在一个特定的范围内。
@Test
public void whenCheckingComparable_thenInRange() {
// ...
assertThat(aComparable).isIn(Range.closed(1, 10));
}
or in a particular list:
或在一个特定的列表中。
@Test
public void whenCheckingComparable_thenInList() {
// ...
assertThat(aComparable).isIn(Arrays.asList(4, 5, 6));
}
We can also test if two Comparable instances are equivalent according to the class’s compareTo() method.
我们还可以根据类的compareTo()方法来测试两个Comparable实例是否相等。
First, let’s modify our User class to implement the Comparable interface:
首先,让我们修改我们的User类以实现Comparable接口。
public class User implements Comparable<User> {
// ...
public int compareTo(User o) {
return this.getName().compareToIgnoreCase(o.getName());
}
}
Now, let’s assert that two users with the same name are equivalent:
现在,让我们断言,两个具有相同名字的用户是等同的。
@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);
}
5.8. Iterable Assertions
5.8.Iterable断言
In addition to asserting the size of an Iterable instance, whether it’s empty or has no duplicates, most typical assertions on an Iterable are that it contains some element:
除了断言Iterable实例的大小、是否为空或没有重复之外,对Iterable最典型的断言是它包含一些元素。
@Test
public void whenCheckingIterable_thenContains() {
List<Integer> aList = Arrays.asList(4, 5, 6);
assertThat(aList).contains(5);
}
that it contains any element of another Iterable:
即它包含另一个Iterable的任何元素。
@Test
public void whenCheckingIterable_thenContainsAnyInList() {
List<Integer> aList = Arrays.asList(1, 2, 3);
assertThat(aList).containsAnyIn(Arrays.asList(1, 5, 10));
}
and that the subject has the same elements, in the same order, like another:
并且,该主体具有相同的元素,以相同的顺序,像另一个主体。
@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();
}
and if it’s ordered using a custom comparator:
以及是否使用自定义比较器进行排序。
@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);
}
5.9. Map Assertions
5.9.地图断言
In addition to asserting that a Map instance is empty or not, or has a specific size; we can check if it has a specific entry:
除了断言一个Map实例为空或不为空,或有一个特定的大小之外,我们还可以检查它是否有一个特定的条目。
@Test
public void whenCheckingMap_thenContainsEntry() {
Map<String, Object> aMap = new HashMap<>();
aMap.put("one", 1L);
assertThat(aMap).containsEntry("one", 1L);
}
if it has a specific key:
如果它有一个特定的键。
@Test
public void whenCheckingMap_thenContainsKey() {
// ...
assertThat(map).containsKey("one");
}
or if it has the same entries as another Map:
或者如果它与另一个Map有相同的条目。
@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.10. Exception Assertions
5.10.异常断言
Only two methods of importance are provided for Exception objects.
对于Exception对象,只提供了两个重要的方法。
We can write assertions addressed to the cause of the exception:
我们可以编写针对异常原因的断言。
@Test
public void whenCheckingException_thenInstanceOf() {
Exception anException
= new IllegalArgumentException(new NumberFormatException());
assertThat(anException)
.hasCauseThat()
.isInstanceOf(NumberFormatException.class);
}
or to its message:
或对其信息。
@Test
public void whenCheckingException_thenCauseMessageIsKnown() {
Exception anException
= new IllegalArgumentException("Bad value");
assertThat(anException)
.hasMessageThat()
.startsWith("Bad");
}
5.11. Class Assertions
5.11.类断言
There’s only one important method for Class assertions with which we can test whether a class is assignable to another:
对于类断言来说,只有一个重要的方法,我们可以用它来测试一个类是否可以被分配给另一个类。
@Test
public void whenCheckingClass_thenIsAssignable() {
Class<Double> aClass = Double.class;
assertThat(aClass).isAssignableTo(Number.class);
}
6. Java 8 Assertions
6.Java 8的断言
Optional and Stream are the only two Java 8 types that Truth supports.
Optional和Stream是Truth支持的唯一两种Java 8类型。
6.1. Optional Assertions
6.1.可选的断言
There are three important methods to verify an Optional.
有三种重要的方法来验证一个选项。
We can test whether it has a particular value:
我们可以测试它是否有一个特定的值。
@Test
public void whenCheckingJavaOptional_thenHasValue() {
Optional<Integer> anOptional = Optional.of(1);
assertThat(anOptional).hasValue(1);
}
if the value is present:
如果该值是存在的。
@Test
public void whenCheckingJavaOptional_thenPresent() {
Optional<String> anOptional = Optional.of("Baeldung");
assertThat(anOptional).isPresent();
}
or if the value is not present:
或如果该值不存在。
@Test
public void whenCheckingJavaOptional_thenEmpty() {
Optional anOptional = Optional.empty();
assertThat(anOptional).isEmpty();
}
6.2. Stream Assertions
6.2.流断言
Assertions for a Stream are very similar to the ones for an Iterable.
Stream的断言与Iterable的断言非常相似。
For example, we can test if a particular Stream contains all objects of an Iterable in the same order:
例如,我们可以测试一个特定的Stream是否以相同的顺序包含一个Iterable的所有对象。
@Test
public void whenCheckingStream_thenContainsInOrder() {
Stream<Integer> anStream = Stream.of(1, 2, 3);
assertThat(anStream)
.containsAllOf(1, 2, 3)
.inOrder();
}
For more examples, please refer to the Iterable Assertions section.
关于更多的例子,请参考Iterable断言部分。
7. Guava Assertions
7.Guava的断言
In this section, we’ll see examples of assertions for the supported Guava types in Truth.
在本节中,我们将看到在Truth中支持的Guava类型的断言的例子。
7.1. Optional Assertions
7.1.可选的断言
There are also three important assertion methods for a Guava Optional. The hasValue() and isPresent() methods behave exactly as with a Java 8 Optional.
对于Guava的Optional,还有三个重要的断言方法。hasValue()和isPresent()方法的行为与Java 8 Optional完全一致。
But instead of isEmpty() to assert that an Optional is not present, we use isAbsent():
但是我们不使用isEmpty()来断言一个Optional不存在,而是使用isAbsent()。
@Test
public void whenCheckingGuavaOptional_thenIsAbsent() {
Optional anOptional = Optional.absent();
assertThat(anOptional).isAbsent();
}
7.2. Multimap Assertions
7.2.Multimap断言
Multimap and standard Map assertions are very similar.
Multimap和标准Map断言非常相似。
One notable difference is that we can get the multiple values of a key within a Multimap and make assertions on those values.
一个明显的区别是,我们可以在一个Multimap中获得一个键的多个值,并对这些值进行断言。
Here’s an example that tests if the values of the “one” key have a size of two:
这里有一个例子,测试 “一 “键的值是否有两个大小。
@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);
}
For more examples, please refer to the Map Assertions section.
更多的例子,请参考Map断言部分。
7.3. Multiset Assertions
7.3.Multiset断言
Assertions for Multiset objects include the ones for an Iterable and one extra method to verify if a key has a particular number of occurrences:
Multiset对象的断言包括Iterable的断言和一个额外的方法来验证一个键是否有特定的出现次数。
@Test
public void whenCheckingGuavaMultiset_thenExpectedCount() {
TreeMultiset<String> aMultiset = TreeMultiset.create();
aMultiset.add("baeldung", 10);
assertThat(aMultiset).hasCount("baeldung", 10);
}
7.4. Table Assertions
7.4.表格断言
Besides checking its size or where it’s empty, we can check a Table to verify if it contains a particular mapping for a given row and column:
除了检查它的大小或它在哪里是空的,我们可以检查一个Table来验证它是否包含一个给定行和列的特定映射。
@Test
public void whenCheckingGuavaTable_thenContains() {
Table<String, String, String> aTable = TreeBasedTable.create();
aTable.put("firstRow", "firstColumn", "baeldung");
assertThat(aTable).contains("firstRow", "firstColumn");
}
or if it contains a particular cell:
或如果它包含一个特定的单元格。
@Test
public void whenCheckingGuavaTable_thenContainsCell() {
Table<String, String, String> aTable = getDummyGuavaTable();
assertThat(aTable).containsCell("firstRow", "firstColumn", "baeldung");
}
Furthermore, we can check if it contains a given row, column, or value. See the source code for the relevant test cases.
此外,我们可以检查它是否包含一个给定的行、列或值。相关测试案例见源代码。
8. Custom Failure Messages and Labels
8.自定义失败信息和标签
When an assertion fails, Truth displays very readable messages denoting exactly what went wrong. However, sometimes is necessary to add more information to those messages to provide more details about what happened.
当断言失败时,Truth会显示非常可读的信息,准确地指出出错的原因。然而,有时有必要在这些信息中添加更多的信息,以提供更多关于发生了什么的细节。
Truth allows us to customize those failure messages:
Truth允许我们定制这些失败信息。
@Test
public void whenFailingAssertion_thenCustomMessage() {
assertWithMessage("TEST-985: Secret user subject was NOT null!")
.that(new User())
.isNull();
}
After running the test, we get the following output:
运行该测试后,我们得到以下输出。
TEST-985: Secret user subject was NOT null!:
Not true that <com.baeldung.testing.truth.User@ae805d5e> is null
Also, we can add a custom label that gets displayed before our subject in error messages. This may come in handy when an object does not have a helpful string representation:
另外,我们可以添加一个自定义标签,在错误信息中显示在我们的主题之前。当一个对象没有一个有用的字符串表示时,这可能会派上用场。
@Test
public void whenFailingAssertion_thenMessagePrefix() {
User aUser = new User();
assertThat(aUser)
.named("User [%s]", aUser.getName())
.isNull();
}
If we run the test, we can see the following output:
如果我们运行该测试,我们可以看到以下输出。
Not true that User [John Doe]
(<com.baeldung.testing.truth.User@ae805d5e>) is null
9. Extensions
9.延长线
Extending Truth means we can add support for custom types. To do this, we need to create a class that:
扩展Truth意味着我们可以添加对自定义类型的支持。要做到这一点,我们需要创建一个这样的类。
- extends the Subject class or one of its subclasses
- defines a constructor that accepts two arguments – a FailureStrategy and an instance of our custom type
- declares a field of SubjectFactory type, which Truth will use to create instances of our custom subject
- implements a static assertThat() method that accepts our custom type
- exposes our test assertion API
Now that we know how to extend Truth, let’s create a class that adds support for objects of type User:
现在我们知道了如何扩展Truth,让我们创建一个类,增加对User类型对象的支持。
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());
}
}
Now, we can statically import the assertThat() method of our custom subject and write some tests:
现在,我们可以静态地导入我们自定义主题的assertThat()方法,并编写一些测试。
@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);
}
10. Conclusion
10.结论
In this tutorial, we explored the possibilities Truth gives us to write more readable tests and failure messages.
在本教程中,我们探讨了Truth给我们带来的可能性,以编写更可读的测试和失败信息。
We showcased the most popular assertion methods for supported Java and Guava types, customized failure messages, and extended Truth with custom subjects.
我们展示了支持的Java和Guava类型的最流行的断言方法、定制的失败信息,以及用自定义主题扩展Truth。
As always, complete source code for this article can be found over on Github.
一如既往,本文的完整源代码可以在Github上找到over。