Spring Boot Testing
@SpringBootTest, MockMvc, slice tests, and Testcontainers — fast, real, layered.
# CLAUDE.md — Spring Boot Testing
## Test layers
- **Unit tests** — plain JUnit + Mockito. No Spring context. Fast.
- **Slice tests** — `@WebMvcTest`, `@DataJpaTest`, `@WebFluxTest`. Load only what's needed for one layer.
- **Integration tests** — `@SpringBootTest` with `@AutoConfigureMockMvc` or full HTTP via `TestRestTemplate`/`WebTestClient`.
Most tests should be unit or slice tests. `@SpringBootTest` is slow — use it sparingly, for end-to-end smoke tests.
## @WebMvcTest
- Loads only MVC infrastructure + the controller under test.
- Mock the service layer with `@MockBean`.
- `MockMvc` for assertions:
```java
mvc.perform(get("/users/{id}", "123"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.email").value("a@b"));
```
- Don't `@SpringBootTest` to test a single controller. Use `@WebMvcTest`.
## @DataJpaTest
- Loads JPA + repositories + an in-memory or testcontainers DB.
- Each test runs in a transaction that rolls back. Don't `commit()`.
- Use **Testcontainers Postgres**, not H2. The dialect differences matter:
```java
@Testcontainers
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserRepositoryTest { ... }
```
## @SpringBootTest
- Full app context. Use for end-to-end flows that span multiple layers.
- `webEnvironment = RANDOM_PORT` and `TestRestTemplate`/`WebTestClient` for real HTTP.
- Override beans with `@TestConfiguration` or `@MockBean` — don't fork production wiring with profiles like `test`.
## Testcontainers
- One container per type per test run. Reuse with `@Container` static + `withReuse(true)`:
```java
static PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>("postgres:16-alpine")
.withReuse(true);
static { POSTGRES.start(); }
```
- Configure via `@DynamicPropertySource`:
```java
@DynamicPropertySource
static void props(DynamicPropertyRegistry r) {
r.add("spring.datasource.url", POSTGRES::getJdbcUrl);
}
```
## Test slice tips
- `@MockBean` adds a mock to the application context. Differs from `@Mock`, which is just a Mockito mock.
- Avoid `@MockBean` in unit tests — it forces context loading. Plain `@Mock` is faster.
- `@SpyBean` for partial mocking. Use sparingly — usually a code smell.
## Speed
- Reuse the application context across tests. Boot caches contexts by configuration; don't make every test unique.
- `@DirtiesContext` invalidates the cache. Use it only when you must — it kills performance.
- Run unit tests in parallel: `junit.jupiter.execution.parallel.enabled=true`.
## Don't
- Don't load the full `@SpringBootTest` context for tests that touch one bean.
- Don't use H2 / HSQLDB for repository tests. Use Postgres via Testcontainers.
- Don't share state across tests. Random execution order is the default.
- Don't `Thread.sleep` to wait for async work. Use `Awaitility` or `CompletableFuture.get(timeout)`.
Other Java templates
Modern Java Rules
Java 21+ defaults: records, sealed types, pattern matching, virtual threads, var.
Java Testing with JUnit 5
JUnit 5 + AssertJ + Mockito + Testcontainers — opinionated test conventions.
Java Virtual Threads & Concurrency
Project Loom virtual threads, structured concurrency, and modern concurrent patterns.
Java Build (Maven & Gradle)
Build conventions, dependency management, multi-module projects, and CI patterns.
Java Streams & Collections
Streams API, immutable collections, Collectors, and idiomatic data manipulation.