Messaging, contract và end-to-end testing
Distributed boundary cần test delivery, compatibility và recovery; một happy-path publish/consume không đủ để chứng minh business effect an toàn.
1. Các lớp test cho messaging
- Unit: handler rule, mapping và idempotency decision trong process.
- Serialization/contract: schema, header, optional field, default và unknown-field policy.
- Broker integration: topic/queue, routing, ack, offset, partition và consumer group.
- Component: retry, DLQ, poison message và side effect boundary.
- E2E: chỉ critical journey xuyên deployment với dữ liệu và ownership rõ.
Broker container không tự chứng minh exactly-once business effect. Cần test duplicate delivery, crash point và invariant của side effect.
2. Delivery call flow
Producer serialize → broker persist/route → consumer poll/deliver → handler side effect → acknowledgment/offset commit.
Crash giữa side effect và ack tạo redelivery. Vì vậy test phải gửi duplicate hoặc restart consumer tại checkpoint, sau đó assert business invariant cuối cùng thay vì chỉ assert message đã được consume.
3. Retry, backoff và DLQ
Phân biệt transient failure với permanent failure. Test số attempt, backoff, header/metadata, ordering và thời điểm chuyển DLQ. Dùng scheduler cấu hình cho test thay vì chờ delay production thật.
- Poison message không được chặn partition/queue vô hạn.
- Retry policy phải có giới hạn và quan sát được.
- DLQ message cần giữ correlation id, original metadata và lý do thất bại.
- Assert duplicate retry không tạo duplicate payment/order side effect.
- Kiểm tra retry storm và backpressure khi downstream chậm.
4. Contract testing
Consumer-driven hoặc schema compatibility test phát hiện breaking change trước deployment. Contract trả lời liệu consumer và provider có đồng ý về request/response semantics hay không; nó không chứng minh auth, network policy, data migration hoặc business journey hoàn chỉnh.
Version field, optionality, default, enum evolution và unknown field phải có policy rõ. Contract test nên chạy như quality gate trước khi publish schema/API mới.
Contract tests answer whether services agree on request and response semantics. They reduce the need for broad cross-service environments, but they do not prove that infrastructure, deployment configuration, and the complete business journey work together.
5. E2E có chọn lọc
Chỉ giữ E2E cho critical journey xuyên deployment. Dùng API/data setup thay UI khi có thể, selector ổn định, tenant/data unique và artifact đầy đủ khi fail.
- Không phụ thuộc external sandbox không ổn định nếu có thể tạo test double ở boundary phù hợp.
- Failure owner phải rõ; nếu E2E đỏ không xác định component thì observability của test environment chưa đủ.
- Thu request/response, event id, trace id, logs và database evidence cần thiết để debug.
- Cleanup dữ liệu theo test run, không để test sau phụ thuộc dữ liệu test trước.
6. Failure modes
- Duplicate/out-of-order delivery: handler không idempotent hoặc giả định ordering toàn cục.
- Schema evolution: consumer cũ vỡ khi field bắt buộc hoặc enum thay đổi.
- Retry storm: backoff không đủ và DLQ không được giám sát.
- Consumer group contention: test dùng chung group làm tranh message và kết quả không deterministic.
- False green: chỉ assert publish/consume mà không assert committed business state.
- E2E flakiness: selector, clock, external dependency hoặc dữ liệu dùng chung không ổn định.
7. Checklist tự đánh giá
- Tôi phân biệt unit, broker integration, contract, component và E2E test.
- Tôi test duplicate delivery, retry/DLQ và idempotency.
- Tôi hiểu contract không thay thế integration/E2E.
- Tôi có schema evolution policy cho optional/default/unknown field.
- Tôi giữ E2E ở critical path và thu đủ artifact khi fail.