1. Overview
Truth is a fluent and flexible open-source testing framework designed to make test assertions and failure messages more readable.
In this article, we’ll explore the key features of the Truth framework and implement examples to showcase its capabilities.
2. Maven Dependencies
First, we need to add the truth and truth-java8-extension to our 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.
3. Introduction
Truth allows us to write readable assertions and failure messages for a variety of classes:
- 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.
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.
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().
4. Getting Started
To start writing assertions, let’s first import Truth‘s entry points:
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.
5. Standard Java Assertions
In this section, we’ll see detailed examples of how to write test assertions for standard Java types.
5.1. Object Assertions
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:
@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.
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.
5.2. Integer, Float, and Double Assertions
Integer, Float, and Double instances can be compared for equality:
@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:
@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
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
Only two relevant methods are provided, isTrue() and isFalse():
@Test public void whenCheckingBoolean_thenTrue() { boolean aBoolean = true; assertThat(aBoolean).isTrue(); }
5.5. String Assertions
We can test whether a String starts with a particular text:
@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.
5.6. Array Assertions
We can check Arrays to see if they are equal to other 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
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.
First, let’s modify our User class to implement the Comparable interface:
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
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:
@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:
@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
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:
@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:
@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
Only two methods of importance are provided for Exception objects.
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
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
Optional and Stream are the only two Java 8 types that Truth supports.
6.1. Optional Assertions
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
Assertions for a Stream are very similar to the ones for an Iterable.
For example, we can test if a particular Stream contains all objects of an Iterable in the same order:
@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.
7. Guava Assertions
In this section, we’ll see examples of assertions for the supported Guava types in Truth.
7.1. Optional Assertions
There are also three important assertion methods for a Guava Optional. The hasValue() and isPresent() methods behave exactly as with a Java 8 Optional.
But instead of isEmpty() to assert that an Optional is not present, we use isAbsent():
@Test public void whenCheckingGuavaOptional_thenIsAbsent() { Optional anOptional = Optional.absent(); assertThat(anOptional).isAbsent(); }
7.2. Multimap Assertions
Multimap and standard Map assertions are very similar.
One notable difference is that we can get the multiple values of a key within a Multimap and make assertions on those values.
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.
7.3. Multiset Assertions
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:
@Test public void whenCheckingGuavaMultiset_thenExpectedCount() { TreeMultiset<String> aMultiset = TreeMultiset.create(); aMultiset.add("baeldung", 10); assertThat(aMultiset).hasCount("baeldung", 10); }
7.4. Table Assertions
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:
@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
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 allows us to customize those failure messages:
@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
Extending Truth means we can add support for custom types. To do this, we need to create a class that:
- 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:
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:
@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
In this tutorial, we explored the possibilities Truth gives us to write more readable tests and failure messages.
We showcased the most popular assertion methods for supported Java and Guava types, customized failure messages, and extended Truth with custom subjects.
As always, complete source code for this article can be found over on Github.