Handbooks / Spring Boot / Chapter 10
Testing and Production
7 pages · ~4 min✓ Reviewed
Builds on Messaging and HTTP Clients.
Spring Boot Testing Mental Model
1. Pick the Narrowest Test
- Unit testno Spring
- Slice testpartial context
- @SpringBootTestfull context
- Pick the narrowest test that proves the behavior
spring-boot-starter-testbundles JUnit 5, AssertJ, Mockito, Hamcrest, JSONassert, JsonPath, Spring Test
2. Full Context and Caching
| webEnvironment | Effect |
|---|---|
MOCK (default) | no real server |
RANDOM_PORT | starts embedded server |
DEFINED_PORT | uses server.port |
NONE | no web layer |
- Config found by searching upward from the test package for
@SpringBootConfiguration - Keep tests in the same package tree as the main app class
Context caching
Identical config reuses one context. A different @MockBean set, properties or profiles builds a new one and slows the suite.
3. Interview Point
- Which test first? Plain unit test; slice next, full context only if needed
- Default webEnvironment?
MOCK, no real server - Why is the suite slow? Many distinct contexts defeat caching
- Test cannot find config? Move it under the main class's package
Slice Tests and MockMvc
1. @WebMvcTest and MockMvc
@WebMvcTest(UserController.class)loads only the web layer- Loaded: controllers,
@ControllerAdvice, Filters, Converters,WebMvcConfigurer - Not loaded: services and repositories
- Supply dependencies with @MockitoBean (Boot 3.4+;
@MockBeandeprecated before it) - Missing mock →
NoSuchBeanDefinitionException
- perform(get)no real HTTP
- DispatcherServletcontroller + advice
- andExpectstatus, jsonPath
java
mockMvc.perform(get("/users/1")) .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("Ann")); mockMvc.perform(post("/users") .contentType(MediaType.APPLICATION_JSON) .content(json));
POST needs contentType, else 415
2. @DataJpaTest
Testcontainers and Integration Tests
1. What and Why
- Testcontainers runs real Postgres, Kafka, Redis in throwaway Docker containers
- Tests match production; Docker must be running
- Pin image tags, never
latest
2. Wiring
java
@SpringBootTest @Testcontainers class OrderIT { @Container @ServiceConnection static PostgreSQLContainer<?> db = new PostgreSQLContainer<>("postgres:16"); }
Boot 3.1+: no manual URL
| Piece | Effect |
|---|---|
@ServiceConnection | datasource auto-configured (3.1+) |
static field | one container per class |
| non-static field |
Actuator, Logging and Externalized Config
1. Actuator
- Add
spring-boot-starter-actuator - Default: only /actuator/health over HTTP
management.endpoints.web.exposure.include=health,info,metrics,prometheusmanagement.server.portkeeps probes off the public port
| Endpoint | Use |
|---|---|
/health | liveness / readiness groups |
/health/liveness, /health/readiness | Kubernetes probes |
/info, /metrics/{name} | app info, one metric |
/loggers | POST changes log level at runtime |
Packaging and Docker Images
1. Fat JAR and Layers
./mvnw packageor./gradlew bootJar→ executable fat JAR- Run it:
java -jar app.jar - war only for external servlet containers
- Layered by default (2.4+): dependencies, spring-boot-loader, snapshot-dependencies, application
- Unchanged dependency layers stay cached in Docker
bash
java -Djarmode=tools -jar app.jar extract --layerssplits the JAR into layer folders
2. Building the Image
./mvnw spring-boot:build-image(orbootBuildImage): no Dockerfile needed- OCI image with JRE, memory calculator, non-root user
- Dockerfile:
ENTRYPOINT ["java","-jar","app.jar"]
Spring Boot Testing and Production Cheat Sheet
1. Pick the test
| Test | Use for |
|---|---|
| Plain unit test | first choice, no Spring |
@WebMvcTest + @MockitoBean | controllers |
@DataJpaTest | repositories |
@SpringBootTest(RANDOM_PORT) + Testcontainers | end-to-end |
415= missingcontentTypeNoSuchBeanDefinitionExceptionin a slice = missing mock- Each distinct
@MockBeanor property set = new cached context
2. Actuator and logging
Check yourself
A @WebMvcTest(UserController.class) test fails at startup with NoSuchBeanDefinitionException for UserService. Why, and what fixes it?
- Slice tests load only the web layer, so services are not loaded
- Add
@MockitoBean UserServiceto the test
perform(post("/users").content(json)) returns 415. What is the bug?
- No content type, so Spring can't pick a JSON converter
- Add
.contentType(MediaType.APPLICATION_JSON)
application.yml sets server.port=8080, env has SERVER_PORT=9090, and you run java -jar app.jar --server.port=7070. Which port wins?
- 7070: command line beats env vars
- Env vars beat application.yml, so 9090 would win without the argument