Assertion Libraries for Java: AssertJ versus Google Truth
When formulating assertions, JUnit quickly reaches its limits. The AssertJ and Google Truth libraries offer new possibilities and better readability.
(Image: hxdbzxy / Shutterstock.com)
- Marco Dahms
Unit tests are a fundamental element in software engineering. They guarantee the functional correctness of the software and help to identify potential errors early on. Assertions play a central role in this by ensuring that the expected conditions are met during test execution.
Unit tests contain assertions that the test framework checks for compliance at the time of the test. A simple example is checking if the return value of a specific method is true. If an assertion is not met, the test aborts execution and is considered failed. If all assertions are met, the test framework executes the test to the end, and it is considered successful.
A unit test can contain multiple assertions, which are typically located at the end of the test method. In rare cases, it is possible for assertions to be checked in the middle of the test case to ensure compliance with intermediate conditions for the test.
Assertion APIs of Test Frameworks Have Gaps
For formulating assertions in the respective programming language, the API of the test framework is usually used. For example, JUnit for Java offers simple APIs to express assertions.
They are usually sufficient for simple tests with a few assertions and have the advantage that they are directly available in the test framework. Therefore, no additional dependencies need to be included in the software project.
(Image:Â laolina / 123rf.com)
The betterCode() Testing 2026 on June 8, 2026, will show how the interplay of people, tools, and processes ensures the success of modern software. The focus will be on testing with and by AI, test automation, and practical reports demonstrating what should actually be tested.
However, if the project is more extensive and the tests are more complex, these APIs reach their limits. Firstly, they are not optimized for readability and understandability. For more complicated assertions, developers have to write a lot of boilerplate code. Furthermore, the error messages for unmet assertions are rather short and abstract, lacking relevant details for error analysis, such as missing elements in a collection. Additionally, JUnit's assertion API is not extensible, meaning it cannot be supplemented with assertions for one's own domain logic.
The following listing shows an example that demonstrates the limitations of the JUnit Assertion API.
@Test
public void testListComparison() {
List<String> expectedList = Arrays.asList("Apple", "Banana", "Cherry");
List<String> actualList = Arrays.asList("Apple", "Grape", "Cherry");
assertEquals(expectedList, actualList, "Die Listen sollten gleich sein");
}
First, two lists containing strings are created. The call to the assertEquals method requires the two lists to be equal. Since the lists contain different elements, the test fails with the following message:
The lists should be equal ==> expected: <[Apple, Banana, Cherry]> but was: <[Apple, Grape, Cherry]>
The message does not clarify which elements in the lists are different. In this highly simplified example, this may be easily recognizable for developers, but in practice, more complex cases often arise. It would be helpful if the message contained the specific information about which elements are different. In this case: the strings Banana and Grape.
Videos by heise
Another assertion could be that a specific element occurs exactly once in a list. With JUnit's API, this requirement cannot be immediately implemented. Developers have to write the necessary code themselves. Assertion libraries like AssertJ or Google Truth do not have such limitations. The APIs of both libraries are optimized for readability and understandability. Their assertions usually read like natural language. In particular, multiple assertions can be chained in one expression, reducing boilerplate code.
Error messages for violated assertions are significantly more detailed, facilitating error analysis. In addition, there are extensive specialized APIs for standard Java types, such as strings, lists, or exceptions.
AssertJ Offers Extensive Assertion APIs
AssertJ is an open-source Java library with an extensive set of assertions and helpful error messages. Its primary goal is to improve the readability of test code. AssertJ Core is the core of AssertJ; in addition, there are other AssertJ modules for libraries like Guava.
The library can be integrated into a Java project as org.assertj:assertj-core via Maven and Gradle. A project with Spring Boot manages the AssertJ version automatically. If necessary, the AssertJ version can be overridden with the property assertj.version.
AssertJ offers a wide selection of different assertion APIs for various standard Java types, including common types like String, List, or Predicate, and primitive types like int or char. These APIs are specialized for the respective types and offer methods that help avoid writing boilerplate code. The following is an overview:
String APIs:
isNotBlank: String is not an empty stringcontains: String contains a substringhasSize: String has a specific lengthisUpperCase: String consists only of uppercase letters
List/Iterable APIs:
contains: List contains elements in any ordercontainsOnly: List contains only specific elements in any ordercontainsExactly: List contains only specific elements in the given order
The variety of assertions thus covers many use cases.
Developers can add additional semantics to assertions by providing a textual description when calling them. This description becomes part of the error message if the assertions are not met. Configuration options allow controlling whether these descriptions are output directly to the standard console or consumed by custom logic in a so-called Description Consumer, for example, to save them in a file.
The entry point for developers is the Assertions class and its assertThat(…) method. The naming assertThat(…) indicates that emphasis is placed on the intuitiveness and readability of assertions. The assertions thus read like natural language. First, assertThat is declared as a static import:
import static org.assertj.core.api.Assertions.assertThat;
Next, in a test method, one can write assertThat(foo)., where foo refers to the value or object on which the assertion is based. Depending on the type of foo, IDEs with correctly configured code completion offer the appropriate assertion APIs. For example, if a LocalDate object is passed as an argument, one receives suggestions like hasMonth or isAfter. This results in an assertion like the following:
assertThat(date).isNotNull().hasMonth(Month.of(1)).isAfter(beginDate);
The assertion can now be read in natural language as: “Ensure that date is not null, has month 1 (January), and falls after another date, beginDate.” Chaining method calls avoid boilerplate code and increase readability. As soon as one of the assertions fails, the subsequent ones are no longer checked.
If developers want to check all assertions before a test aborts, they use SoftAssertions. To achieve this, they create an object of type SoftAssertions, call the desired assertion methods on it, and finally trigger the final check with assertAll. AssertJ then compiles an overview of all failed assertions. This approach is particularly suitable for complex test cases, as it provides a direct overview of all errors and prevents the need to restart the test after each error correction.
Furthermore, AssertJ can be extended with assertions for one's own application domain. Writing Custom Assertions allows developing assertion methods tailored to one's own data model. In the case of appointment management software, one could consider the following assertions:
assertThat(appointment).isDue()assertThat(appointment).isCancelled()
Appointment would be a class from one's own domain model, and isDue and isCancelled would be self-developed assertion methods. This approach increases the readability and understandability of unit tests by reflecting one's own application domain from the production code also in the tests. To implement a custom assertion, developers must derive a new class from the abstract class AbstractAssert, implement a constructor and a static assertThat method, and also all required assertion methods (such as isDue, isCancelled, etc.).
Google Truth Focuses on Understandable and Clear APIs
Another assertion library for Java is Google Truth, developed and maintained by Google's Guava team. It is used in the majority of tests in the Google codebase. The focus is on readable assertions and error messages. Truth supports many standard Java types and types from the Guava library. Truth is integrated into Maven or Gradle projects via the artifact com.google.truth:truth.
To use Truth in a test, you first need the assertThat method, provided via a static import:
import static com.google.common.truth.Truth.assertThat;
Similar to AssertJ, an object can be passed as an argument to assertThat, after which assertion APIs corresponding to the argument's type are provided. A simple example from the Truth documentation is checking if a string starts with a specific substring:
String string = "awesome";
assertThat(string).startsWith("awe");
To add more semantics to error messages for violated assertions, developers can provide a suitable description. To achieve this, they import the assertWithMessage method and call it:
import static com.google.common.truth.Truth.assertWithMessage;
assertWithMessage("Without me, it's just aweso")
.that(string)
.contains("me");
Truth also allows extending with custom assertions. In the Truth data model, one writes a custom Subject for this purpose. The custom Subject class must be derived from the Subject class. Additionally, a static helper method and a constructor are needed. Furthermore, specific assertion methods must be added. The Truth documentation refers to the reference example of EmployeeSubject.
For extensive tests with many assertions, where a complete check of all assertions is helpful, Truth uses the Expect class, initialized via a JUnit Rule annotation:
@Rule public final Expect expect = Expect.create();
On the Expect object, similar to assertThat, one can pass an argument and formulate suitable assertions. Unfortunately, this approach is only supported up to JUnit 4. From JUnit 5 onwards, the Rule annotation is no longer possible, and Truth currently (as of April 2026) offers no alternative implementation for it. Since JUnit 6 was released in September 2025, using JUnit 4 is not recommended. Consequently, Expect can no longer be used meaningfully.