Part 02 · Testing & Code Quality · 2.1.04

Mockito và test doubles tại boundary

Mock là công cụ kiểm soát collaborator ở một boundary có chủ đích, không phải bản sao của hệ thống production và cũng không nên thay thế mọi dependency.


1. Taxonomy và lựa chọn

DoubleVai tròDùng khiCạm bẫy
StubTrả response định trước.Điều khiển nhánh logic và failure case.Có thể bỏ qua behavior thật của dependency.
MockGhi và verify interaction.Interaction chính là contract, ví dụ không charge hai lần.Dễ couple test với call graph.
FakeImplementation đơn giản nhưng hoạt động.Cần state/behavior nhẹ, ví dụ in-memory repository.Semantics có thể khác technology thật.
SpyBọc object thật và cho phép override một phần.Legacy seam có chủ đích.Vô tình gọi code thật hoặc tạo test khó hiểu.

2. Call flow và strict stubbing

Test tạo mock → khai báo stubbing → system under test gọi collaborator → Mockito ghi invocation → assertion kiểm tra outcome và chỉ verify interaction quan trọng. Strict stubbing giúp phát hiện setup thừa hoặc argument không khớp; lenient không nên được dùng để che design smell.

when(paymentGateway.charge(any())).thenReturn(success());

service.placeOrder(command);

verify(paymentGateway).charge(
    argThat(req -> req.idempotencyKey().equals("order-42")));
Thứ tự ưu tiên: assert observable outcome trước; chỉ verify interaction khi lời gọi đó có ý nghĩa business hoặc architectural, chẳng hạn payment không được charge lần hai hay event không được publish sau validation failure.

3. Verify đúng chỗ

4. Spy, final/static và async

Spy có thể gọi code thật nếu không stub đúng kiểu. Dùng doReturn(...).when(spy) khi cần tránh lời gọi thật trong lúc stubbing; đừng để spy che một boundary thiết kế chưa rõ.

Mock final/static có thể hữu ích ở legacy seam nhưng làm tăng khả năng khóa implementation. Ưu tiên dependency injectable và architectural boundary rõ ràng.

Async verification với timeout có thể che race condition nếu chỉ “đợi đủ lâu”. Tốt hơn là expose completion signal, awaitility/coordination rõ ràng và assert eventual invariant.

5. Failure modes

6. Câu trả lời phỏng vấn mẫu

Over-mocking couples tests to implementation details. I mock architectural boundaries or expensive nondeterministic dependencies, not every collaborator. For persistence behavior, SQL semantics, or framework configuration, I prefer an integration test against the real technology.

7. Checklist tự đánh giá

Nguồn tham khảo