Use AssertJ for more explicit tests

  FR
 4 min. to read
 chop
 No comment yet
  (last mod.: 2021-11-29)

We won’t discuss the merits of testing today, I’ll just suggest a library that helps them write differently and, according to me, makes them more comprehensible.

My Issue with Tests #

In my whole career, JUnit is the only framework I used for testing Java code (well, two frameworks, since I’ve used JUnit 4 and have more recently grown accustomed to Jupiter). Call me ignorant if you will, but I love this framework; it provides most of the bases I need for testing.

One detail that quite often comes back to bother me, though, is the writing of the assertions. Reading them is not always obvious, for several reasons:

  • There are common mistakes, like inverting the “expected” and the “actual” parameters, which can make debugging a nightmare if you trust the code and don’t take a step back about what it’s supposed to do.
  • It’s probably among the most procedural pieces of code we have to write in Java. Just a sequence of instructions.
  • I’ve never known a developer (me included) who bothered to write messages for unverified assertions, and even comments are few (but I guess I could write an entire post on that).

I could go on, but you get the gist.

There are many ways to write tests that are more understandable. We may talk about Cucumber and Gherkin, but not today. Today, I’ll focus on fluent interfaces and more especially on AssertJ.

Fluent Interface to the Rescue #

Do you know what fluent interface is? It belongs to the object-oriented world and relies heavily on chaining method calls. For instance, I suppose the Builder pattern could be considered fluent interface:

1final Book gameOfThrones = Book.builder()
2    .title("A Game of Thrones")
3    .author("George R. R. Martin")
4    .series("A Song of Ice and Fire")
5    .build();

I like this approach better than what I knew before that. Calling setters one by one always seemed a bit heavy to me…

1final Book gameOfThrones = new Book();
2gameOfThrones.setTitle("A Game of Thrones");
3gameOfThrones.setAuthor("George R. R. Martin");
4gameOfThrones.setSeries("A Song of Ice and Fire");

… and the all-args constructor also allow for immutable objects but can quickly become verbous and non-yieldy:

  • You need to know the position of each argument: code reviews become harder without documentation or smart IDE.
  • It declares either many variants or you might leave many fields null.
1// I don't know the publication year, leave empty
2final Book gameOfThrones = new Book("A Game of Thrones", "George R. R. Martin", null, "A Song of Ice and Fire");

This was a crude example of what can be done with a fluent interface. The Stream API is another.

However, there’s an interesting fact with several implementations I’ve seen: you get the feeling that, instead of writing a list of statement, you write a sentence, something that even a non-coder could read. AssertJ’s among these implementations.

Getting Started with AssertJ #

What would you get if you wrote your tests with AssertJ? Well, let’s see a few examples:

 1assertThat(tyrion)
 2    .isNotEqualTo(robert)
 3    .isIn(lannisterFamily);
 4
 5assertThat(melisandre.getName())
 6    .startsWith("Mel")
 7    .endsWith("dre")
 8    .isEqualToIgnoringCase("melisandre");
 9
10assertThat(nedsChildren)
11    .hasSize(5)
12    .contains(robb, arya)
13    .doesNotContain(joffrey);

Now, imagine writing this with JUnit’s assertions and tell me which you prefer.

To get started with AssertJ, you need to import your dependency, so in Maven:

1<dependency>
2  <groupId>org.assertj</groupId>
3  <artifactId>assertj-core</artifactId>
4  <version>3.20.2</version>
5  <scope>test</scope>
6</dependency>

And then, in your test classes, replace JUnit’s assertions with one import:

1import static org.assertj.core.api.Assertions.assertThat;

From there, you can use your editor autocompletion, the official documentation or some renowned tutorial to know what assertions are made available to you.

assertThat is the main method, but several others are available. From the documentation:

 1import static org.assertj.core.api.Assertions.assertThat;  // main one
 2import static org.assertj.core.api.Assertions.atIndex; // for List assertions
 3import static org.assertj.core.api.Assertions.entry;  // for Map assertions
 4import static org.assertj.core.api.Assertions.tuple; // when extracting several  properties at once
 5import static org.assertj.core.api.Assertions.fail; // use when writing exception tests
 6import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; // idem
 7import static org.assertj.core.api.Assertions.filter; // for Iterable/Array assertions
 8import static org.assertj.core.api.Assertions.offset; // for floating number assertions
 9import static org.assertj.core.api.Assertions.anyOf; // use with Condition
10import static org.assertj.core.api.Assertions.contentOf; // use with File assertions

Now, give AssertJ a spin and tell us what you think. As for me, I know that, since I discovered it, I have a tendency to replace all my JUnit assertions. You can see examples of JUnit 5 used with AssertJ in my Download proxy project.