# Vì sao code tích hợp bạn bàn giao cần ít mock hơn bạn nghĩ

> Vladimir Khorikov đưa ra một quy tắc mock rất gọn, kèm theo cái giá mà bộ test nào cũng phải trả.

Bản gốc: https://fdetimes.net/vi/sach-khoa-hoc/sach-unit-testing-principles-practices-patterns-khorikov/

Chỉ nên mock những dependency bên ngoài mà các hệ thống khác cũng dùng chung. Vladimir Khorikov, tác giả cuốn *Unit Testing: Principles, Practices, and Patterns*, viết quy tắc này trên blog Enterprise Craftsmanship năm 2020. Nếu bạn là FDE vừa viết xong một service nối hệ thống của khách với sản phẩm của công ty, câu này có thể thay đổi cả bộ test bạn sắp bàn giao.

Lý do nằm ở hoàn cảnh bàn giao. Khi bạn rời site khách hàng, đội kỹ thuật của khách sẽ là người sửa code tích hợp đó. Bộ test là thứ duy nhất báo cho họ biết họ vừa làm hỏng gì. Bộ test mock tràn lan, cứ refactor là đỏ dù chẳng có bug, sẽ nhanh chóng bị tắt đi. Đến lúc đó thì có test cũng như không.

## Mock hay không tùy vào ai dùng chung dependency

Khorikov chia các dependency ngoài tiến trình làm hai loại. Managed dependency là thứ chỉ truy cập được qua ứng dụng của bạn, ví dụ database riêng của service. Unmanaged dependency là thứ mà các ứng dụng khác cũng truy cập được. Từ đó ông rút ra quy tắc: dùng instance thật cho managed, chỉ mock unmanaged.

Thử hình dung một service bạn viết cho một công ty phân phối. Service nhận đơn hàng, lưu vào Postgres riêng, rồi đẩy sang API của hệ thống ERP mà kế toán và kho của khách cùng đọc.

Postgres là managed, vì không ai ngoài service của bạn chạm vào nó. ERP là unmanaged, vì những lời gọi bạn gửi sang đó là hợp đồng mà các hệ thống khác cũng phụ thuộc vào.

Áp quy tắc vào ví dụ này, test sẽ chạy trên Postgres thật và mock ERP ở biên, kiểm tra nội dung request gửi đi có đúng không. Giả sử mấy tháng sau đội của khách đổi tên một cột trong migration.

Nếu bạn đã mock database, test vẫn xanh còn production thì hỏng. Nếu chạy trên Postgres thật, test đỏ ngay trong CI. Ngược lại, mock ERP vẫn hợp lý: test chỉ cần chứng minh service giữ đúng hợp đồng với bên ngoài.

**Điểm mấu chốt:** Một bộ test đáng tin chỉ đỏ khi có bug thật, chứ không đỏ mỗi lần bạn refactor.

## Không bộ test nào được cả ba thứ cùng lúc

Câu hỏi tiếp theo là làm sao chấm một test tốt hay dở. Theo một bản ghi chép tóm tắt sách, Khorikov đưa ra bốn trụ cột: bảo vệ khỏi regression, chịu được refactor, phản hồi nhanh và dễ bảo trì. Điểm mấu chốt là ba trụ cột đầu loại trừ nhau. Bạn chỉ được chọn hai trong ba, giống kiểu đánh đổi của định lý CAP.

Quay lại service đơn hàng. Một test end-to-end gọi vào sandbox ERP thật bắt được nhiều bug và không vỡ khi bạn đổi cấu trúc code bên trong, nhưng nó chậm. Một test nhỏ mock mọi thứ chạy rất nhanh, nhưng bám chặt vào chi tiết cài đặt nên vỡ ngay khi bạn tách một hàm. Kiểu test thứ hai là thứ đội của khách sẽ ghét nhất.

Hiểu được sự đánh đổi này, bạn sẽ không còn tìm một loại test "đúng" duy nhất. Với service đơn hàng, cách chia hợp lý là nhiều test nhanh cho logic nghiệp vụ thuần, cộng một số ít integration test chạy trên database thật.

Trong danh sách nội dung sách, Manning có nêu việc dùng integration test để kiểm tra toàn bộ hệ thống, bên cạnh cách tránh anti-pattern và refactor test song song với code production.

## Ai nên đọc, và đọc theo thứ tự nào

Cuốn sách do Manning xuất bản tháng 1/2020, dày 304 trang. Trên trang của mình, Khorikov gọi đây là hướng dẫn trọn vẹn về unit testing, và cho rằng các quy tắc lập trình nên được suy ra từ lập luận nền tảng.

Manning nói rõ cuốn sách viết cho người đã biết unit test cơ bản. Nếu bạn chưa từng viết test, hãy học một framework trước rồi quay lại sau. Ví dụ trong sách viết bằng C#, nhưng nhà xuất bản khẳng định chúng áp dụng được cho mọi ngôn ngữ.

Nếu thời gian của bạn bị lịch khách hàng chia nhỏ, có thể đọc theo thứ tự này. Bắt đầu với bài blog "When to Mock", đọc hết trong một buổi tối.

Sau đó đọc phần sách về bốn trụ cột, vì đó là bộ tiêu chí bạn sẽ dùng để chấm từng test. Cuối cùng đọc phần mock và integration test, với code bạn đang viết mở ngay bên cạnh.

Lời hứa của Manning là giúp bạn cải thiện bộ test sẵn có bằng các thực hành hiện đại, và lời hứa này khớp với thực tế của FDE. Hiếm khi bạn được viết test từ đầu. Thường thì bạn kế thừa một repo của khách, đã có sẵn những test chậm, giòn và mock khắp nơi.

## Biến cuốn sách thành điểm cộng khi đi phỏng vấn

Phần lớn ứng viên chỉ ghi trong CV một dòng kiểu "viết unit test". Bạn có thể ghi cụ thể hơn, chẳng hạn: integration test chạy trên database thật, mock chỉ đặt ở biên API dùng chung với hệ thống khác, và bộ test vẫn ổn định khi đội của khách refactor sau bàn giao.

Dòng đó cho nhà tuyển dụng thấy bạn hiểu chuyện bàn giao, chứ không chỉ biết cú pháp.

Trong phỏng vấn, nếu được hỏi về chiến lược test cho một hệ thống tích hợp, hãy trả lời bằng sự đánh đổi chứ không bằng tỷ lệ coverage. Nói rõ bạn chọn hai trong ba trụ cột nào cho từng lớp test và vì sao. Đó đúng là cách suy nghĩ đi từ nền tảng mà Khorikov đề cao.

Khi bạn rời site, code của bạn vẫn ở lại. Bộ test là thứ sẽ thay bạn trả lời những câu hỏi mà đội của khách chưa kịp hỏi.

**Thử ngay tuần này:**

- Liệt kê mọi dependency ngoài tiến trình trong service tích hợp bạn đang làm, rồi đánh dấu từng cái là managed hoặc unmanaged
- Chọn một test đang mock database riêng của ứng dụng và viết lại để nó chạy trên database thật trong container
- Đọc bài 'When to Mock' trên enterprisecraftsmanship.com trước khi quyết định có mua sách không

## Nguồn

- [Unit Testing Principles, Practices, and Patterns (Manning)](https://www.manning.com/books/unit-testing)

- [Enterprise Craftsmanship: Unit Testing book page](https://enterprisecraftsmanship.com/book/)

- [When to Mock](https://enterprisecraftsmanship.com/posts/when-to-mock/)

- [Four Pillars of Good Unit Tests](https://notesbylex.com/four-pillars-of-good-unit-tests)
