Unravel › Java concepts › Testing
AssertJ fluent assertions
AssertJ makes test assertions read like sentences: assertThat(orders).hasSize(2).extracting(Order::id).containsExactly(1, 2).
Learn these first
At a glance
Instead of a bare "check" stamp, a proofreader who writes in the margin: "line 3 should be 42 but is 41".
- 1Start with assertThat(actual) (a static import from org.assertj.core.api.Assertions) and chain checks: isEqualTo, isNotNull, contains, startsWith, isBetween, isCloseTo(1.0, within(0.01))
- 2Collections: hasSize, containsExactly (order matters), containsExactlyInAnyOrder, extracting("name"), allMatch(...), noneMatch(...)
- 3Exceptions: assertThatThrownBy(() -> service.find(0)).isInstanceOf(NotFoundException.class).hasMessageContaining("0")
- 4usingRecursiveComparison().isEqualTo(expected) compares objects field by field without needing equals
- The IDE completes the available checks for the type under test, so you discover the API by typing a dot
- Use SoftAssertions to report all failures of a test at once
- assertThat is overloaded per type and returns a specific assertion class (ListAssert, StringAssert...), each a fluent builder that throws an AssertionError with a detailed diff on the first failed check
import static org.assertj.core.api.Assertions.*;import java.util.List;import org.junit.jupiter.api.Test;class OrderTest { record Order(int id, String customer) {} @Test void ordersAreFound() { List<Order> orders = List.of(new Order(1, "ann"), new Order(2, "bob")); assertThat(orders).hasSize(2).extracting(Order::customer).containsExactly("ann", "bob"); assertThatThrownBy(() -> orders.get(5)).isInstanceOf(IndexOutOfBoundsException.class); }}- line 9 uses hasSize, the core of AssertJ fluent assertions
- line 10 uses assertThatThrownBy, the core of AssertJ fluent assertions
Read the full explanation (about 1 min)
AssertJ makes test assertions read like sentences: assertThat(orders).hasSize(2).extracting(Order::id).containsExactly(1, 2). Failure messages show exactly what differed.
A mental model
Instead of a bare "check" stamp, a proofreader who writes in the margin: "line 3 should be 42 but is 41".
How it works
Start with assertThat(actual) (a static import from org.assertj.core.api.Assertions) and chain checks: isEqualTo, isNotNull, contains, startsWith, isBetween, isCloseTo(1.0, within(0.01)).
Collections: hasSize, containsExactly (order matters), containsExactlyInAnyOrder, extracting("name"), allMatch(...), noneMatch(...).
Exceptions: assertThatThrownBy(() -> service.find(0)).isInstanceOf(NotFoundException.class).hasMessageContaining("0").
usingRecursiveComparison().isEqualTo(expected) compares objects field by field without needing equals.
Going deeper
The IDE completes the available checks for the type under test, so you discover the API by typing a dot. This is its main advantage over JUnit's assertEquals(expected, actual), which is easy to write with the arguments swapped.
Use SoftAssertions to report all failures of a test at once. Name the failing check with .as("order total").
Assertion style does not replace good tests: assert on behaviour (results, state, interactions), not on internal details.
Under the hood
assertThat is overloaded per type and returns a specific assertion class (ListAssert, StringAssert...), each a fluent builder that throws an AssertionError with a detailed diff on the first failed check.
Common mistakes
- Building an assertion but forgetting the final check (
assertThat(x)alone asserts nothing). - Using
containsExactlywhen order does not matter. - Mixing JUnit and AssertJ assertion styles in the same test class.
How Unravel recognises it
When you paste code, Unravel looks for these signs of assertj fluent assertions. Spotting them yourself is a good habit when you read unfamiliar code:
- Method calls:
isEqualTo()isNotNull()hasSize()containsExactly()containsExactlyInAnyOrder()extracting()assertThatThrownBy()assertThatExceptionOfType()usingRecursiveComparison()hasMessageContaining()isInstanceOf()isNull()isEmpty()isPresent()and 3 more
Part of a learning path
- Errors, logging and tests in depth · previous: JUnit 5: parameterized tests and lifecycle · next: Mockito: matchers, verification, spies
Related concepts
Frequently asked questions
What is AssertJ fluent assertions?
AssertJ makes test assertions read like sentences: assertThat(orders).hasSize(2).extracting(Order::id).containsExactly(1, 2). Failure messages show exactly what differed.
What are the most common mistakes with AssertJ fluent assertions?
Building an assertion but forgetting the final check (assertThat(x) alone asserts nothing). Using containsExactly when order does not matter. Mixing JUnit and AssertJ assertion styles in the same test class.
How can I see AssertJ fluent assertions in real code?
Paste your own Java into Unravel and it will point out where assertj fluent assertions appears, explain how it works at the detail level you choose, and draw the flow of the code around it.