Spring TestContext và test slices
Spring testing là bài toán chọn context boundary nhỏ nhất nhưng vẫn chứng minh được wiring và framework behavior cần thiết.
1. TestContext architecture
JUnit extension tạo và điều phối TestContextManager. Các listener tham gia dependency injection, transaction, SQL script, dirtiness và cleanup. Spring cache application context theo configuration key giữa các test class.
Profile, property override, mock bean hoặc @DirtiesContext có thể làm đổi key hoặc loại context khỏi cache, khiến suite chậm. Context caching là một phần của test architecture, không chỉ là tối ưu build.
@DirtiesContext như cleanup mặc định và kiểm tra số lần full context khởi động trong CI report.2. Chọn slice hay full context
| Công cụ | Boundary | Dùng khi | Trade-off |
|---|---|---|---|
@WebMvcTest | MVC mapping, validation, serialization, filter/security liên quan. | Controller contract và HTTP error response. | Không chứng minh repository hoặc toàn bộ application wiring. |
@DataJpaTest | JPA mapping, repository query, constraint và persistence context. | SQL/JPA behavior cần kiểm tra isolation. | Không chứng minh web/security layer. |
@JsonTest | JSON serialization/deserialization. | Schema, custom serializer và null/format behavior. | Không chứng minh controller hoặc network. |
@RestClientTest | HTTP client với mock server. | Request mapping, error mapping và retry client. | Không chứng minh remote service thật. |
@SpringBootTest | Full application context và component behavior. | Wiring hoặc interaction nhiều bean cần full context. | Startup chậm, failure khó định vị, dễ tạo suite thiếu precision. |
@SpringBootTest không nên là mặc định cho mọi test. Chọn slice khi risk nằm ở một boundary cụ thể; chỉ dùng full context khi wiring giữa nhiều layer là chính điều cần chứng minh.
3. MockMvc và real server
MockMvc đi qua DispatcherServlet pipeline nhưng không mở socket. Nó phù hợp để kiểm tra mapping, validation, exception handler và response serialization. Random-port test khởi động embedded server và HTTP stack thật hơn, phù hợp với network configuration, filter container hoặc client/server integration.
Quyết định dựa trên risk, không dựa trên sở thích: nếu socket, port, TLS hoặc server container là failure mode, MockMvc không đủ fidelity.
4. Database và Testcontainers
H2 hoặc database in-memory có SQL dialect và behavior khác PostgreSQL/MySQL. Với query, constraint, locking, isolation hoặc transaction semantics quan trọng, dùng Testcontainers chạy cùng database engine với production.
@Testcontainers
class OrderRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:17-alpine");
}
Container test vẫn cần schema setup, unique test data, cleanup và giới hạn thời gian hợp lý. Không nên biến container thành global mutable fixture mà test không có ownership.
5. Transaction-managed tests
Test-managed transaction thường rollback sau test, hữu ích cho isolation nhưng có thể che commit visibility, after-commit listener và async work. Nếu production behavior phụ thuộc commit thật, hãy commit chủ động rồi đọc từ transaction khác.
- Test rollback bằng exception đúng rule, không chỉ assert database cuối test đã rỗng.
- Kiểm tra visibility từ transaction khác khi cần chứng minh commit.
- Cẩn thận với preemptive timeout: code chạy ở thread khác có thể thoát transaction test.
- Không để framework rollback tự động che mất behavior production.
6. Failure modes
- Full context cho mọi test: feedback chậm và lỗi khó định vị.
- Mock bean quá nhiều: đổi cache key và che wiring thật.
@DirtiesContextlạm dụng: suite mất lợi ích context cache.- Persistence context cache entity: test đọc entity đã cached và tạo false confidence.
- In-memory database gap: test pass nhưng SQL/locking/constraint khác production.
- Transaction thread mismatch: timeout hoặc async chạy ngoài transaction mà test tưởng đang được rollback.
7. Checklist tự đánh giá
- Tôi chọn được slice phù hợp với boundary cần chứng minh.
- Tôi giải thích được khác biệt giữa MockMvc và random-port test.
- Tôi biết khi nào H2 không đủ và cần database thật qua Testcontainers.
- Tôi phân biệt rollback của test với commit visibility trong production.
- Tôi có thể tìm context cache miss và nguyên nhân suite Spring chạy chậm.