Part 02 · Testing & Code Quality · 2.1.06

Infrastructure test bằng Testcontainers

Container tạm thời tăng fidelity với production, nhưng chỉ đáng tin khi lifecycle, readiness, networking và data isolation được thiết kế rõ.


1. Architecture và lifecycle

Test process dùng Docker-compatible runtime để tạo container, map port ngẫu nhiên, theo dõi resource và dọn khi test kết thúc. Generic container cung cấp primitive; module PostgreSQL, Kafka hoặc Redis thêm connection API và default phù hợp với technology đó.

Image tag phải được pin gần với production. Dùng tag trôi nổi như latest làm test thay đổi theo thời gian và khiến failure khó tái hiện.

@Testcontainers
class OrderRepositoryTest {
    @Container
    static PostgreSQLContainer<?> postgres =
        new PostgreSQLContainer<>("postgres:17-alpine");
}

2. Readiness không phải startup

Process started chưa chắc service đã nhận request. Wait strategy phải quan sát signal phù hợp: log message, listening port, health endpoint hoặc query có ý nghĩa. Chờ một khoảng sleep cố định thường tạo flaky test và làm CI chậm.

3. Scope container và data isolation

Per-test container cô lập tốt nhưng chậm. Static per-class hoặc singleton suite nhanh hơn nhưng cần reset schema/data, quản lý sequence, connection state, broker offset và external side effect.

Transaction rollback không reset mọi thứ: sequence, background worker, broker offset và side effect bên ngoài vẫn có thể tồn tại. Reuse container có thể hữu ích ở local, nhưng test CI không được phụ thuộc vào container đã chạy từ trước.

Nguyên tắc: tối ưu reuse chỉ sau khi test đã deterministic với lifecycle rõ. Tốc độ không đáng đổi lấy hidden state và test order dependency.

4. Networking và configuration

Test process thường kết nối tới container qua mapped host port; container-to-container dùng Docker network alias và internal port. Không hard-code localhost hoặc port cố định.

5. CI operations

6. Failure modes

7. Checklist tự đánh giá

Nguồn tham khảo