JUnit 5 Assertions — Complete Guide

3 minute read

Assertions are the verdict of your test — they determine pass or fail. JUnit 5 provides rich assertions, and AssertJ adds fluent readability.


Basic Assertions

import static org.junit.jupiter.api.Assertions.*;

@Test
void basicAssertions() {
    // Equality
    assertEquals(4, 2 + 2);
    assertEquals("hello", "HELLO".toLowerCase());
    assertNotEquals("foo", "bar");

    // Boolean
    assertTrue(10 > 5);
    assertFalse("".length() > 0);

    // Null checks
    assertNull(null);
    assertNotNull("value");
    assertNotNull(new ArrayList<>());
}

Custom Failure Messages

@Test
void withMessages() {
    User user = userService.findById(1L);

    assertNotNull(user, "User with ID 1 should exist in the database");
    assertEquals("Alice", user.getName(),
        () -> "Expected user name to be Alice but got " + user.getName());
}

Tip: Use lambda messages (() -> "...") for expensive string construction — only evaluated on failure.


assertThrows — Testing Exceptions

@Test
void shouldThrowOnNullInput() {
    IllegalArgumentException ex = assertThrows(
        IllegalArgumentException.class,
        () -> StringUtils.reverse(null)
    );

    assertEquals("Input cannot be null", ex.getMessage());
}

@Test
void shouldNotThrow() {
    assertDoesNotThrow(() -> StringUtils.reverse("hello"));
}

assertAll — Grouped Assertions

Without assertAll, the test stops at the first failure. With it, all assertions run:

@Test
void shouldCreateUserWithAllFields() {
    User user = userService.createUser("Alice", "alice@example.com");

    assertAll("user",
        () -> assertNotNull(user.getId()),
        () -> assertEquals("Alice", user.getName()),
        () -> assertEquals("alice@example.com", user.getEmail()),
        () -> assertNotNull(user.getCreatedAt()),
        () -> assertTrue(user.isActive())
    );
}

If multiple assertions fail, you see all failures — not just the first one.


assertTimeout — Performance Assertions

@Test
void shouldCompleteQuickly() {
    assertTimeout(Duration.ofSeconds(2), () -> {
        // This must complete within 2 seconds
        service.processOrder(orderId);
    });
}

// With return value
@Test
void shouldReturnResultQuickly() {
    String result = assertTimeout(Duration.ofMillis(500), () -> {
        return service.fetchData();
    });
    assertNotNull(result);
}

assertTimeoutPreemptively() aborts after the timeout (runs in a separate thread). Use with caution — it can leave resources in an inconsistent state.


Collection Assertions

@Test
void collectionAssertions() {
    List<String> expected = List.of("Alice", "Bob", "Charlie");
    List<String> actual = userService.getAllNames();

    // Exact match (order matters)
    assertIterableEquals(expected, actual);

    // Array equality
    assertArrayEquals(
        new int[]{1, 2, 3},
        new int[]{1, 2, 3}
    );
}

Floating-Point Assertions

@Test
void floatingPointEquality() {
    // ❌ Fragile — floating-point precision issues
    // assertEquals(0.3, 0.1 + 0.2);

    // ✅ Use delta
    assertEquals(0.3, 0.1 + 0.2, 0.0001);
}

assertSame / assertNotSame — Reference Equality

@Test
void referenceEquality() {
    String a = new String("hello");
    String b = new String("hello");

    assertEquals(a, b);       // ✅ passes — same value
    assertNotSame(a, b);     // ✅ passes — different objects

    String c = a;
    assertSame(a, c);        // ✅ passes — same reference
}

AssertJ — Fluent Assertions (Alternative)

AssertJ provides a more readable, discoverable API:

<dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <version>3.26.0</version>
    <scope>test</scope>
</dependency>
import static org.assertj.core.api.Assertions.*;

@Test
void assertJExample() {
    String name = "Alice";

    assertThat(name)
        .isNotNull()
        .isNotEmpty()
        .startsWith("Ali")
        .hasSize(5);
}

@Test
void assertJCollections() {
    List<User> users = userService.findAll();

    assertThat(users)
        .hasSize(3)
        .extracting(User::getName)
        .containsExactly("Alice", "Bob", "Charlie");
}

@Test
void assertJExceptions() {
    assertThatThrownBy(() -> userService.findById(null))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessageContaining("cannot be null");
}

Why AssertJ?

Feature JUnit 5 AssertJ
Readability OK ✅ Fluent chain
IDE autocomplete Limited ✅ Start typing assertThat(x).
Collection assertions Basic ✅ extracting, filteredOn, containsOnly
Exception assertions assertThrows ✅ assertThatThrownBy with chaining
Soft assertions assertAll ✅ SoftAssertions

Assertion Cheat Sheet

Assertion Use When
assertEquals(expected, actual) Comparing values
assertTrue(condition) Boolean check
assertNull(value) Null check
assertThrows(Exception.class, ...) Expected exception
assertAll(...) Multiple assertions, see all failures
assertTimeout(duration, ...) Performance check
assertIterableEquals(...) List/collection equality
assertThat(x).isEqualTo(y) AssertJ fluent style

Common Mistakes

Mistake Fix
assertEquals(actual, expected) — wrong order Expected first, actual second
Missing delta for doubles Use assertEquals(0.3, val, 0.001)
Single assertion per assertAll Group related assertions together
Not checking exception message Use assertThrows + getMessage()

Key Takeaways

  1. assertEquals — expected first, actual second.
  2. assertThrows — test exceptions and verify messages.
  3. assertAll — run all assertions, see all failures at once.
  4. Use delta for floating-point comparisons.
  5. Consider AssertJ for fluent, readable assertions.

What’s Next?

Next: Parameterized Tests — run the same test with different inputs automatically.

Updated: