Java’s AssertJ: The Secret Weapon for Painless Testing
Writing clean and expressive tests with Java’s AssertJ library. Let’s learn how to test collections, primitives, exceptions, and domain objects!
In this article, we’ll discuss some features of Java’s AssertJ library. We’ll see how easy it is to test various types of data and to understand what went wrong when the tests are failing.
Moreover, we’ll learn how to create our own custom asserts based on the domain models, and use them to improve the readability of our tests.
1. Testing Collections
Let’s assume we want to write an assertion to check that a list contains two exact values. In most cases, I have seen code using the static assertEquals method0 imported from org.junit.jupiter.api.Assertions:
// import static org.junit.jupiter.api.Assertions.assertEquals;
@Test
void test() {
List<Integer> evenNumbers = List.of(1, 2, 3, 4, 5).stream()
.filter(nr -> nr % 2 == 0)
.toList();
assertEquals(2, evenNumbers.size());
assertEquals(2, evenNumbers.get(0));
assertEquals(4, evenNumbers.get(1));
}This assertion has 2 problems: first, it is hard to understand, and we cannot quickly say what is being tested, especially if the use case is a bit more complex.
Secondly, if this test fails, it will take us a while to figure out what went wrong and how to fix it. The <click here to see differences> link is not very helpful either. (for demonstration purposes, I will add “0” to the initial list, but without updating the assertion)

From this point of view, AssertJ will be a huge upgrade! To create an “Assert” object we can use the factory method assertThat:
org.assertj.core.api.Assertions.assertThat( argument )
Then, based on the type of the argument object, a different type of Assert will be returned. Each of these Assert objects will provide a nice API to test the argument. For instance, if we create it using a list, we’ll have list-related methods:

Therefore, we can replace the previous asserts with containsExactly — this will check the size, the elements, and their order:
@Test
void test2() {
List<Integer> evenNumbers = List.of(1, 2, 3, 4, 5).stream()
.filter(nr -> nr % 2 == 0)
.toList();
assertThat(evenNumbers)
.containsExactly(2, 4);
}Moreover, if add back the “0” element and make the test fail, the message will be way more explicit and easy to understand:

Testing Strings and Numbers
Similarly, testing Strings will be much easier with AsserJ. I highly encourage you to check it out and read through its API. Here are some helpful methods:
@Test
void testStrings() {
assertThat(" ignore whitespace ")
.isEqualToIgnoringWhitespace("ignore whitespace");
assertThat("IGNORE case")
.isEqualToIgnoringCase("ignore case");
assertThat("").isBlank();
assertThat("<p> this is a test. </p>")
.startsWith("<p>")
.endsWith("</p>");
assertThat("aGVsbG8gd29ybGQ=")
.isBase64();
}Additionally, AssertJ allows you to write fluent asserts. In other words, we can chain asserts in a nice, declarative way:
@Test
void testNumbers() {
assertThat(5)
.isOdd()
.isPositive()
.isLessThan(10);
}Testing Exceptions
AssertJ’s “ThrowableAssert” provide a very nice API for testing the exceptions as well, we can check the exception type, the error message, and the stack tarce:
@Test
void testIfHasPropertyName() {
assertThatThrownBy(() -> testedFunction())
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("test exception")
.hasStackTraceContaining("ro.etrandafir.medium.assertions.AssertionsTest.testedFunction");
}
void testedFunction() {
throw new IllegalArgumentException("this is a test exception");
}Testing Domain Objects
Finally, let’s learn how to create custom asserts for our domain objects. In my opinion, creating custom asserts is very important if you want to have expressive tests that help you understand the domain logic.
For a quick example, let’s assume we have a the following Account model:
record Account(Long id, String name, Status status) {
static Account newAccount(String name) {
return new Account(null, name, Status.PENDING);
}
}One way to create custom asserts for it would be to extend AssertJ’s AbstractAssert:
public class AccountAssert extends AbstractAssert<AccountAssert, Account> {
public AccountAssert(Account account) {
super(account, AccountAssert.class);
}
public static AccountAssert assertThat(Account actual) {
return new AccountAssert(actual);
}
public AccountAssert hasName(String expectedName) {
super.isNotNull();
Assertions.assertThat(actual.name())
.isEqualTo(expectedName);
return this;
}
public AccountAssert isNotActive() {
super.isNotNull();
Assertions.assertThat(actual.status())
.isNotEqualTo(Account.Status.ACTIVE);
return this;
}
public AccountAssert hasId(long expectedId) {
super.isNotNull();
Assertions.assertThat(actual.id())
.isEqualTo(expectedId);
return this;
}
public AccountAssert hasNoId() {
super.isNotNull();
Assertions.assertThat(actual.id()).isNull();
return this;
}
}As a result, we will be able to test the Account class in a fluent, expressive way:
@Test
void testDomainObjects() {
Account canceledAccount = new Account(1l, "john doe", Status.CLOSED);
AccountAssert.assertThat(canceledAccount)
.hasId(1l)
.hasName("john doe")
.isNotActive();
Account newAccount = Account.newAccount( "jane doe");
AccountAssert.assertThat(newAccount)
.hasNoId()
.hasName("jane doe")
.isNotActive();
}Of course, this is a very simple example, but it can potentially be extended to cover many common business checks.
Conclusion
In this article, we’ve explored various ways of creating unit-test assertions using Java’s AssertJ. We explored its fluent API and learned how to test collections, numbers, Strings, and exceptions.
After that, we created our own custom assertion and we saw how to leverage it to build highly expressive tests.
Thank You!
Thanks for reading the article and please let me know what you think! Any feedback is welcome.
If you want to read more about clean code, design, unit testing, functional programming, and many others, make sure to check out my other articles. Do you like the content? Consider following or subscribing to the email list.
Finally, if you consider becoming a Medium member and supporting my blog, here’s my referral.
Happy Coding!
