Không chạy test thì cả đội chỉ đang "tưởng tượng" code đúng
Reviewer đọc code và suy luận; nhưng suy luận có thể sai. Chỉ có chạy test thật mới cho bằng chứng khách quan. Tester là vai trò biến "tôi nghĩ nó đúng" thành "nó xanh, đây là bằng chứng". Đây là cổng chất lượng cứng nhất trong đội.
Tester làm hai việc
- Sinh test từ
acceptancecủa task (kể cả ca biên). - Chạy test trong môi trường cách ly và báo cáo kết quả có cấu trúc.
Ví dụ báo cáo của Tester
{
"passed": false,
"total": 5, "failed": 1,
"failures": [
{"test":"test_add_duplicate_sku",
"expected":"qty=3","actual":"qty=1",
"trace":"add_item ghi đè thay vì cộng dồn"}
]
}
Trường passed cho orchestrator biết ngay nên commit hay quay lại Coder; failures cho Coder biết chính xác sửa gì.
Sinh test từ acceptance
Nhớ lại Bài 4: acceptance được viết để "thành một test". Giờ Tester hiện thực hóa điều đó:
# acceptance: "Thêm trùng sku thì cộng dồn qty"
def test_add_duplicate_sku():
cart = Cart()
cart.add_item("A", 1)
cart.add_item("A", 2)
assert cart.items["A"].qty == 3
Các bước triển khai Tester
- Chuẩn bị sandbox chạy test (Docker hoặc venv riêng, timeout rõ ràng).
- Cho Tester
acceptance+ code, yêu cầu sinh test bao gồm ca biên và ca lỗi. - Chạy test, thu output thô (stdout/stderr, exit code).
- Ép Tester tóm tắt output thành JSON
{passed, failures[]}— orchestrator chỉ đọc JSON này. - Nếu đỏ, chuyển
failuresvề Coder; nếu xanh, cho phép commit. - Lưu test đã sinh vào repo để chạy lại (regression) ở các task sau.
Chống "test giả xanh"
Rủi ro lớn: Coder có thể "gian lận" bằng cách viết code làm test luôn pass, hoặc Tester viết test rỗng. Cách phòng:
- Tách người viết test khỏi người viết code: Tester sinh test độc lập từ acceptance, không nhìn cách Coder cài đặt (chỉ nhìn giao diện).
- Yêu cầu tối thiểu số ca kiểm thử (ví dụ 1 happy path + 1 ca biên + 1 ca lỗi).
- Kiểm tra assertion: từ chối test không có
assert. - Chạy test trên phiên bản code cũ (biết sai) để chắc test bắt được lỗi (mutation-check nhẹ).
Khung tư duy: "Bằng chứng, không phải niềm tin"
Mỗi khi định đánh dấu task hoàn thành, hỏi: tôi có log test xanh làm bằng chứng không? Nếu chỉ có "Reviewer nói ổn", task chưa xong.
Sai lầm thường gặp
- Không chạy test thật, chỉ để LLM "đọc và đoán" là đúng.
- Chạy test trên môi trường thật thay vì sandbox, gây rủi ro phá dữ liệu.
- Chấp nhận test rỗng/không assert, tạo cảm giác an toàn giả.
- Không lưu test lại, mất khả năng bắt lỗi hồi quy ở task sau.
- Để Tester đọc cách cài đặt của Coder, test bị "nhiễm" và bỏ sót lỗi thiết kế.
Test xanh giả: bốn dạng, và cách bắt từng dạng
"Chống test giả xanh" là mục quan trọng nhất của bài, và nó có bốn dạng cụ thể:
| Dạng | Biểu hiện | Cách bắt |
|---|---|---|
| Không có assertion | Test chạy code, không kiểm gì | Từ chối test không có assert |
| Assertion luôn đúng | assert result is not None | Yêu cầu assert vào giá trị cụ thể |
| Test bắt chước cài đặt | Mock đúng cách Coder viết, nên luôn pass | Tester sinh test từ acceptance, không đọc code |
| Test không chạy đường cần kiểm | Pass vì nhánh lỗi không bao giờ vào | Chạy test trên code cũ (biết sai) — phải FAIL |
is not None luôn an toàn và luôn pass. Câu lệnh chặn: "Mọi assert phải so với một giá trị cụ thể mong đợi, không dùng kiểm tra tồn tại."Dạng thứ tư cần phép kiểm mạnh nhất và cũng rẻ: chạy test mới trên phiên bản code trước khi sửa. Nếu nó pass, test đang không kiểm thứ cần kiểm. Đây là một dòng trong quy trình Tester và nó bắt được phần lớn test vô dụng.
Tại sao Tester phải mù với cài đặt
Điểm này dễ bị xem là tiểu tiết, nhưng nó là kiến trúc. Nếu Tester đọc code của Coder, nó sẽ viết test mô tả code đó thay vì mô tả yêu cầu. Hệ quả: test pass với một cài đặt sai, vì nó đã học sai từ chính cài đặt đó.
Cụ thể trong sơ đồ đa agent: Tester chỉ nhận acceptance và giao diện công khai. Nó không nhận diff. Việc này làm Tester yếu hơn ở việc viết test chạy được ngay, và mạnh hơn nhiều ở việc bắt lỗi thật — đó là đánh đổi đúng.
Ba loại ca tối thiểu: cụ thể là gì
"1 happy path + 1 ca biên + 1 ca lỗi" cần định nghĩa rõ, vì agent sẽ hiểu lỏng:
- Happy path: đầu vào hợp lệ điển hình, kết quả đúng như acceptance nói.
- Ca biên: giá trị ở ranh giới — rỗng, 0, một phần tử, giá trị lớn nhất, trùng lặp. Với ví dụ "thêm trùng sku thì cộng dồn qty", ca biên là thêm trùng ba lần.
- Ca lỗi: đầu vào không hợp lệ, và test kiểm rằng hệ thống báo lỗi đúng cách, không phải kiểm rằng nó không sập.
assert không raise là một test kém. assert raise ValidationError với thông điệp X là một test thật.Chỉ số cho Tester
Một con số nên theo qua các task: tỉ lệ lỗi do Reviewer hoặc người phát hiện sau khi Tester đã báo xanh. Nếu con số đó cao, vấn đề không ở Coder — nó ở chất lượng test, và bạn đang có một cảm giác an toàn giả trong toàn bộ dây chuyền. <!--/les-append-->