All Java templates

Java Testing with JUnit 5

JUnit 5 + AssertJ + Mockito + Testcontainers — opinionated test conventions.

DevZone Tools2,480 copiesUpdated Mar 26, 2026Java
# CLAUDE.md — Java Testing with JUnit 5

## Stack

- **JUnit 5** (Jupiter) for the test runner. No more JUnit 4 in new code.
- **AssertJ** for fluent assertions. Replaces Hamcrest and JUnit's `assertEquals`.
- **Mockito** for mocks. `@Mock` + `@InjectMocks`, or `Mockito.mock(Class)` for fine control.
- **Testcontainers** for real Postgres/Redis/Kafka in tests. SQLite/H2 lie.

## File layout

- Test classes live in `src/test/java`, mirroring `src/main/java` package by package.
- `XxxTest.java` for unit tests, `XxxIT.java` (or `XxxIntegrationTest.java`) for integration. Maven Failsafe runs `*IT` separately.
- One test class per production class. Group by behavior with nested `@Nested` classes.

## JUnit 5 idioms

- `@DisplayName` for human-readable names — they show up in IDE and CI reports.
- `@Nested` to group related cases:
  ```java
  @Nested @DisplayName("when user is admin")
  class WhenAdmin { @Test void canDelete() { ... } }
  ```
- `@ParameterizedTest` with `@MethodSource`, `@ValueSource`, or `@CsvSource`. Don't write copy-paste tests differing only by input.
- `@TestMethodOrder` only when you actually need ordering. Random by default.

## AssertJ

- `assertThat(actual).isEqualTo(expected)`. Don't mix in `assertEquals(...)`.
- Chain assertions: `assertThat(list).hasSize(3).contains("a", "b").doesNotContain("c");`.
- For exceptions, use `assertThatThrownBy(() -> ...)` — message and type in one statement.
- `as("context")` adds a description that shows up in failure messages.

## Mockito

- `@ExtendWith(MockitoExtension.class)` on the class. Use `@Mock`, `@Spy`, `@InjectMocks`.
- Stub with `when(...).thenReturn(...)`. Verify with `verify(mock).method(...)`.
- `lenient()` only when a stub legitimately isn't always called. Otherwise strict mode catches dead stubs.
- Don't mock value types or DTOs. Build them with `new` or a builder.

## Testcontainers

- Start containers with `@Testcontainers` + `@Container`. JUnit 5 manages lifecycle.
- Reuse containers across tests with `withReuse(true)` and the singleton pattern. Boot cost amortizes fast.
- Per-test database: each test runs in a transaction that rolls back. Or truncate in `@BeforeEach`.

## Speed

- Run tests in parallel: `junit.jupiter.execution.parallel.enabled=true`.
- Tag slow tests `@Tag("slow")` and exclude from default runs.
- Profile with `--scan-classpath` and Surefire's `time` reporter.

## Don't

- Don't `Thread.sleep` to wait for async results. Use `CompletableFuture.get(timeout)`, Awaitility, or test scheduling APIs.
- Don't depend on test execution order. Assume any order.
- Don't mock the framework (Spring, Hibernate). Use real instances or thin slice tests.
- Don't write tests that pass with `@Disabled`. Either fix the test or delete it.

Other Java templates