skip to content

How do you assert on collections and iterables with AssertJ (contains, containsExactly, containsExactlyInAnyOrder, hasSize, extracting)?

level: middleimportance: must knowfreq 65%

answer

  1. contains = present + extras OK; containsExactly = exact + order
  2. containsExactlyInAnyOrder for Sets / unordered results (anti-flake)
  3. hasSize / isEmpty / hasSameSizeAs
  4. extracting(User::getName) maps each element to a field
  5. Multiple fields -> extracting(...).containsExactly(tuple(...))

basics

~20 s

Call assertThat on the collection, then chain: hasSize(n) for count, contains(a, b) to check elements are present, containsExactly(...) for the exact contents in order, containsExactlyInAnyOrder(...) ignoring order, and extracting("field") to assert on one property of each element.

solid answer

~40 s

assertThat(collection) returns an iterable/list assertion with element-aware methods. hasSize(n) checks the count. contains(a, b) asserts those elements are present (order and extras allowed); containsOnly ignores order and duplicates; containsExactly(a, b, c) demands exactly those elements in that order; containsExactlyInAnyOrder(...) demands the same multiset regardless of order. Use containsExactlyInAnyOrder when order is not guaranteed (e.g. a HashSet or a DB query without ORDER BY) to avoid flaky tests. extracting maps each element to a property (by name or method reference) so you assert on derived values: assertThat(users).extracting(User::name).containsExactly("Ann", "Bo"). extracting with multiple fields plus tuple(...) checks several properties per element. flatExtracting flattens nested collections. These read clearly and produce element-level failure diffs.

code

java · 16 lines
java
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;

List<User> users = repo.findAll();

assertThat(users).hasSize(2);

// order-insensitive: safe for sets / unordered queries
assertThat(users)
    .extracting(User::getName)
    .containsExactlyInAnyOrder("Ann", "Bo");

// multiple properties per element
assertThat(users)
    .extracting(User::getName, User::getAge)
    .containsExactly(tuple("Ann", 30), tuple("Bo", 25));

go deeper

for a junior

Can use hasSize and contains to check a collection has expected elements.

for a middle

Distinguishes contains vs containsOnly vs containsExactly vs containsExactlyInAnyOrder and uses extracting to assert on element fields.

for a senior

Chooses order-sensitive vs insensitive deliberately to avoid flakiness, uses extracting/tuple/flatExtracting for object graphs, and reviews tests for these pitfalls.

for a principal

Sets team conventions on collection assertions (e.g. prefer InAnyOrder unless ordering is contractual), evaluates extracting vs custom assertions for domain types, and guards CI against order-dependent flakiness.

## The element-aware assertion object When you write `assertThat(someCollection)` where the value is a `List`, `Set`, array, or any `Iterable`, AssertJ returns an **iterable assertion** (e.g. `ListAssert`, `IterableAssert`). Its chained methods understand *elements*, so you assert about contents, size, and ordering rather than calling `.size()` yourself. ## Size `hasSize(n)` asserts the element count equals `n`. Related: `isEmpty()`, `isNotEmpty()`, `hasSizeGreaterThan(n)`, `hasSameSizeAs(otherCollection)`. ## Membership family — the key distinctions These differ along three axes: **must all expected be present? are extras allowed? does order matter? are duplicates significant?** - `contains(a, b)` — a and b must be present **somewhere**; **extra** elements are fine; order ignored. - `containsOnly(a, b)` — every element is one of a/b and both appear; **no extras**; **order and duplicates ignored**. - `containsExactly(a, b, c)` — the collection equals exactly `[a, b, c]` in **that order**, no more, no fewer. Use only when order is deterministic. - `containsExactlyInAnyOrder(a, b, c)` — same **multiset** (counts matter) but **order ignored**. This is the right choice for `Set`s, unordered streams, or DB results without an `ORDER BY` — using `containsExactly` there makes the test **flaky** (passes/fails depending on iteration order). - `containsAnyOf(a, b)` — at least one present. `doesNotContain(x)` — x absent. `containsSequence(a, b)` — consecutive subsequence; `containsSubsequence(a, c)` — same order but gaps allowed. **Duplicates:** `containsExactly` and `containsExactlyInAnyOrder` treat the collection as a multiset, so `[1,1,2]` is not exactly the same as `[1,2]`. `contains`/`containsOnly` ignore duplicate counts. ## extracting — assert on a derived property of each element Often you do not care about whole objects, only one field. `extracting` transforms each element into that field, yielding a new collection assertion: ```java assertThat(users).extracting(User::getName) .containsExactly("Ann", "Bo"); ``` You can extract by **method reference** (`User::getName`, type-safe, refactor-friendly) or by **property/field name string** (`extracting("name")`, reflection-based, not refactor-safe). With **multiple** properties, pair it with `tuple(...)`: ```java assertThat(users) .extracting(User::getName, User::getAge) .containsExactly(tuple("Ann", 30), tuple("Bo", 25)); ``` `flatExtracting` extracts a collection-valued property and **flattens** all of them into one stream for a single assertion (e.g. all roles across all users). ## Why this matters - **Readability:** the intent ("these exact names, this order") is explicit. - **Failure quality:** AssertJ reports *which* element was missing or unexpected, not just `false`. - **Flakiness control:** choosing the order-sensitive vs order-insensitive variant deliberately is a real test-quality concern. ## Common mistake Using `containsExactly` on a `Set` or a stream from a `HashMap` — iteration order is unspecified, so the test passes locally and fails in CI. Prefer `containsExactlyInAnyOrder` unless order is contractually guaranteed.

  • When would containsExactly cause a flaky test and what do you use instead?
    When the collection's iteration order is not guaranteed (HashSet, HashMap values, a query without ORDER BY), containsExactly depends on incidental ordering and can fail intermittently. Use containsExactlyInAnyOrder to compare as a multiset without caring about order.
  • What is the difference between extracting by method reference and by string name?
    extracting(User::getName) is type-safe and survives renames via the IDE; extracting("name") uses reflection on a property/field name, so a rename silently breaks it at runtime and there is no compile-time check.

saying these in an interview costs you the question

  • Thinking contains(a, b) forbids extra elements (it does not; use containsExactly/containsOnly)
  • Using containsExactly on a Set or unordered result and getting flaky failures
  • Believing containsExactlyInAnyOrder ignores duplicate counts (it compares as a multiset)
  • Assuming extracting changes the original collection (it returns a new assertion view)

context