Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Fluent assertions with AssertJ

JUnit assertions are enough for simple checks, but for collections and objects they become wordy. AssertJ is a library of fluent assertions: you start with assertThat(value), and the IDE suggests the checks that make sense for that type. The tests read almost like sentences, and failure messages show the whole picture. AssertJ works inside ordinary JUnit tests.

Adding the dependency

Add to dependencies in pom.xml:

<!-- pom.xml -->
<dependency>
  <groupId>org.assertj</groupId>
  <artifactId>assertj-core</artifactId>
  <version>3.27.7</version>
  <scope>test</scope>
</dependency>

All methods are imported from one class: import static org.assertj.core.api.Assertions.*;.

Strings and numbers

// src/test/java/com/example/blog/PostAssertJTest.java
@Test
void strings() {
    String slug = Slugs.of("Hello, World!");
 
    assertThat(slug)
            .isEqualTo("hello-world")
            .startsWith("hello")
            .doesNotContain(" ");
}
 
@Test
void bigDecimals() {
    BigDecimal total = new PriceCalculator().total(new BigDecimal("12.00"), 3);
 
    assertThat(total).isEqualByComparingTo("36");
}

Checks are chained: each one returns the same assertion object. isEqualByComparingTo compares a BigDecimal by value, ignoring the scale — 36 and 36.00 are equal.

Collections

// src/test/java/com/example/blog/PostAssertJTest.java
@Test
void lists() {
    InMemoryPostRepository repository = new InMemoryPostRepository();
    repository.save(new Post(null, "First", "anna", true));
    repository.save(new Post(null, "Second", "oleg", false));
 
    List<Post> posts = repository.findAll();
 
    assertThat(posts)
            .hasSize(2)
            .extracting(Post::title)
            .containsExactly("First", "Second");
    assertThat(posts)
            .filteredOn(Post::published)
            .extracting(Post::title, Post::author)
            .containsExactly(tuple("First", "anna"));
}

extracting turns a list of posts into a list of titles, filteredOn keeps only published posts. With several extractors you get tuple values. Other useful checks: contains, containsExactlyInAnyOrder, isEmpty, allMatch.

Exceptions

// src/test/java/com/example/blog/PostAssertJTest.java
@Test
void exceptions() {
    PostService service = new PostService(new InMemoryPostRepository(), (to, message) -> { });
 
    assertThatThrownBy(() -> service.find(42))
            .isInstanceOf(PostNotFoundException.class)
            .hasMessage("Post not found: 42");
}

This is the AssertJ version of assertThrows plus the message check in one chain.

Failure messages

If the list had "Second" and the test expected "Third", AssertJ explains in detail:

org.opentest4j.AssertionFailedError:
 
Expecting actual:
  ["First", "Second"]
to contain exactly (and in same order):
  ["First", "Third"]
but some elements were not found:
  ["Third"]
and others were not expected:
  ["Second"]

as() before a check adds a label to the message — handy when a test has several similar checks:

assertThat(Slugs.of("Hello, World!")).as("slug").startsWith("world");
java.lang.AssertionError:
[slug]
Expecting actual:
  "hello-world"
to start with:
  "world"

Common mistake

assertThat by itself checks nothing. This line looks like a check, but the test passes whatever the slug is:

assertThat(Slugs.of("Hello World").contains(" "));

The boolean result of contains goes into assertThat, and no check method is called after it. Always finish the chain: assertThat(slug).doesNotContain(" ") or assertThat(slug.contains(" ")).isFalse().

Thanks for reading!

Donate to the site — we work hard for you.