Công cụ AI Coding

TDD với AI: viết test cùng Claude Code theo red-green-refactor (2026)

Aug 14, 202613 phút đọc

TDD với AI là viết test (đang fail) trước, rồi để Claude Code viết code tối thiểu cho test xanh, cuối cùng refactor mà vẫn giữ xanh - vòng lặp Red → Green → Refactor. Test đóng vai "definition of done" mà AI không thể tự phong. Lưu ý thật quan trọng nhất: Claude mặc định thích viết code trước, nên bạn phải chủ động ép nó viết test trước bằng prompt tách phase và quy ước trong CLAUDE.md.

TDD với AI là gì?

TDD (Test-Driven Development) là cách viết code trong đó bạn viết test trước - để nó fail - rồi mới viết đủ code cho test pass, sau đó dọn dẹp (refactor) mà không làm test đỏ lại. Đó là TDD "cổ điển" đã có từ lâu. Còn "TDD với AI" nghĩa là bạn để Claude Code (hoặc trợ lý AI tương tự) làm cả hai việc: viết test và viết implementation theo test đó, còn bạn giữ vai trò định nghĩa yêu cầu và kiểm chứng.

Điểm khác biệt cốt lõi so với "nhờ AI code rồi bảo nó viết test sau" là thứ tự. Trong TDD, test viết trước chính là bản đặc tả (spec) chạy được: nó mô tả chính xác input, output và các trường hợp biên trước khi có một dòng code thật nào. Claude Code sẽ code để làm test đó xanh, chứ không phải viết bừa rồi tự chấm điểm cho mình.

Nếu bạn chưa quen với cách làm việc theo quy trình cùng AI, nên đọc trước bài vibe coding là gì và khác gì làm việc có quy trình để thấy TDD nằm ở đâu trong bức tranh lớn. TDD chính là một trong những "kỷ luật" giúp vibe coding không biến thành đống code không ai kiểm soát được.

Vì sao TDD cực hợp với Claude Code?

Claude Code mạnh nhất khi "định nghĩa hoàn thành" là một thứ chạy được và tự kiểm tra được - mà test chính là thứ đó. Khi bạn nói "làm cho hàm này chạy đúng", AI phải đoán ý bạn. Khi bạn nói "làm cho 8 test này pass", AI có một mục tiêu nhị phân rõ ràng: đỏ hay xanh, không có vùng xám để nó "tưởng là xong".

Có ba lý do khiến cặp đôi này ăn ý:

  • Test là spec + lưới an toàn cùng lúc. Test mô tả yêu cầu, đồng thời bắt lỗi ngay khi AI sửa nhầm chỗ khác. Với codebase mà AI liên tục chỉnh sửa, lưới an toàn này là thứ giữ bạn không "sửa một chỗ, hỏng ba chỗ".
  • Vòng lặp phản hồi chặt. Viết test → chạy → đọc lỗi → sửa tới xanh. Claude Code chạy được test trong terminal và đọc output thật, nên nó tự sửa qua nhiều vòng mà không cần bạn copy-paste lỗi thủ công.
  • Chống AI làm sai yêu cầu. Đây là điểm ít người nói thẳng: test viết trước ép bạn (và AI) phải làm rõ yêu cầu trước khi code. Rất nhiều lỗi "AI code đúng kỹ thuật nhưng sai ý" biến mất khi yêu cầu đã được đóng băng thành test.

Chính Anthropic cũng liệt kê TDD như một workflow được khuyến nghị khi làm việc với Claude Code trong tài liệu Claude Code best practices (Anthropic, 2025): viết test, xác nhận chúng fail, rồi mới viết code cho tới khi test pass. Đây không phải mẹo tự nghĩ ra - nó là cách nhà sản xuất công cụ gợi ý dùng.

Chu trình Red-Green-Refactor với Claude Code

Toàn bộ TDD gói gọn trong ba bước, lặp lại cho từng mẩu tính năng nhỏ. Với Claude Code, mỗi bước tương ứng một prompt và một kỳ vọng output rõ ràng.

Bước 1 - Red: viết test fail (chưa có code)

Bạn yêu cầu Claude viết test cho hành vi mong muốn, và chỉ test thôi. Chạy test này phải đỏ, vì implementation chưa tồn tại.

Viết unit test cho hàm validate_email(email) trong src/validators.py bằng pytest.
Phủ các case: email hợp lệ, thiếu @, thiếu domain, chuỗi rỗng, None.
Test PHẢI fail vì hàm chưa tồn tại. ĐỪNG viết code cho validate_email.

Kỳ vọng: một file test_validators.py với vài case, và khi chạy pytest sẽ báo ImportError hoặc test đỏ. Đỏ ở đây là đúng - nó chứng minh test thật sự kiểm tra thứ chưa có.

Bước 2 - Green: viết code tối thiểu cho pass

Giờ mới cho AI viết implementation, và nhấn mạnh "tối thiểu" để tránh nó tự thêm tính năng thừa.

Viết implementation TỐI THIỂU cho validate_email để toàn bộ test trong
test_validators.py pass. Chạy `pytest -q` và dán kết quả thật cho tôi.
Đừng thêm tính năng ngoài phạm vi test.

Kỳ vọng: code vừa đủ, và output pytest chuyển sang xanh. Bắt AI dán kết quả chạy thật, đừng tin lời "đã pass".

Bước 3 - Refactor: dọn code, giữ xanh

Khi đã xanh, bạn có lưới an toàn để dọn dẹp thoải mái.

Refactor validate_email cho gọn và dễ đọc (tách regex ra hằng số, đặt tên rõ).
KHÔNG đổi hành vi. Chạy lại `pytest -q` sau khi sửa để chứng minh vẫn xanh.

Kỳ vọng: code sạch hơn, test vẫn xanh nguyên. Nếu một test đỏ khi refactor, đó là dấu hiệu bạn vừa đổi hành vi ngoài ý muốn - sửa lại ngay.

Phiên test-first thật với Claude Code (từng bước)

Lý thuyết là vậy, giờ là một phiên thật trên tính năng nhỏ: một hàm parse_price("1.299.000đ") trả về số nguyên 1299000. Đây là loại logic thuần, có input/output rõ - mảnh đất lý tưởng cho TDD.

Bước 1 - yêu cầu test đỏ. Mình mở Claude Code trong thư mục dự án và gõ:

Viết pytest cho parse_price(s) trong src/pricing.py:
- "1.299.000đ" -> 1299000
- "50.000 đ" -> 50000
- "0đ" -> 0
- chuỗi không có số -> raise ValueError
Chỉ viết test. Hàm chưa tồn tại nên test phải fail.

Bước 2 - yêu cầu code tối thiểu cho xanh. Sau khi xác nhận test đỏ đúng như mong đợi:

Viết implementation tối thiểu cho parse_price để 4 test pass.
Chạy `pytest -q` và dán output. Đừng xử lý case ngoài test.

Claude viết một hàm strip ký tự không phải số, ép kiểu int, và raise ValueError khi rỗng. Nó tự chạy test và trả về kết quả xanh.

Bước 3 - refactor. Mình bảo Claude tách phần bóc số ra một helper và thêm docstring, yêu cầu chạy lại test để chứng minh không đổi hành vi. Test vẫn 4 xanh. Cả phiên mất vài phút, và điều quan trọng là mình chưa bao giờ phải tin lời "đã xong" - mọi khẳng định đều có output pytest thật kèm theo.

Cách ÉP Claude Code viết test TRƯỚC (không bị viết code trước)

Đây là chỗ đau đầu nhất và cũng là lý do nhiều team bỏ TDD sau vài ngày: Claude Code, giống hầu hết trợ lý AI, có xu hướng nhảy thẳng vào viết implementation rồi mới thêm test cho có. Nó được huấn luyện để "giải quyết vấn đề", mà với nó viết code trông giống giải quyết vấn đề hơn là viết test. Có ba cơ chế ép hiệu quả:

1. Prompt tách phase rõ ràng. Đừng gộp "viết hàm X và test cho nó" thành một câu - đó là mời AI viết code trước. Tách hẳn: bước Red chỉ nói "viết test FAIL cho X, CHƯA viết code", và chỉ khi test đỏ xong mới sang bước Green. Câu "chưa viết code" viết hoa hoặc in đậm giúp AI tuân thủ tốt hơn đáng kể.

2. Quy ước test-first trong CLAUDE.md. Đây là cách bền nhất - ghi luật một lần, áp cho mọi phiên. Thêm đoạn này vào file CLAUDE.md của dự án:

## Quy ước TDD (bắt buộc)
- Luôn viết test TRƯỚC implementation cho mọi logic/hàm mới.
- Trình tự: (1) viết test fail, (2) chạy `pytest -q` xác nhận đỏ,
 (3) viết code tối thiểu cho pass, (4) refactor mà vẫn xanh.
- KHÔNG viết implementation trong bước Red.
- KHÔNG sửa test chỉ để code hiện có pass.
- Sau mỗi thay đổi phải chạy test THẬT và dán output, không nói "đã pass" suông.

Nếu bạn chưa từng cấu hình file này, xem hướng dẫn viết file CLAUDE.md chuẩn (ép test-first) - nó là công tắc bật/tắt kỷ luật cho toàn dự án.

3. Phase-gate bằng subagent. Cách nâng cao: dùng một subagent chuyên viết test, không cho nó thấy kế hoạch implementation. Khi "người viết test" không biết code sẽ trông thế nào, test bám vào hành vi mong muốn chứ không bám vào code định viết - tránh được test viết vừa khít để "cho xanh". Cách tổ chức nhiều agent theo phase nằm trong quy trình brainstorm → plan → cook → ship.

Lỗi thường gặp khi TDD với AI (anti-pattern)

TDD với AI thất bại theo những kiểu rất dễ đoán. Nhận diện sớm để không tự lừa mình rằng "đang làm TDD":

Anti-patternVì sao saiCách sửa
Gộp Red + Green một lượtAI viết code trước rồi mới thêm test khớp code - không còn là test-first, mất giá trị specTách hẳn hai prompt; xác nhận test đỏ xong mới cho viết code
Bảo AI "viết test" cho code có sẵnĐó là test-after, chỉ khoá lại hành vi hiện tại (kể cả bug), không dẫn dắt thiết kếVới code cũ: viết test cho hành vi đúng mong muốn trước khi sửa
Verification gap: AI báo "done" nhưng chưa chạy testAI có thể tưởng tượng kết quả pass mà không thực thiLuôn bắt chạy test thật + dán output; dựng tầng kiểm: lint → unit → e2e
Over-mockMock quá nhiều khiến test chỉ kiểm tra mock, không kiểm tra logic thậtChỉ mock ranh giới I/O (network, DB); logic thuần thì test thật
Test trivial "cho xanh"assert True hoặc test lặp lại chính code - vô nghĩaMỗi test phải khẳng định một hành vi cụ thể, có case biên và case lỗi

Trong đó verification gap là nguy hiểm nhất vì nó âm thầm. Khi kẹt ở một test cứ đỏ mãi hoặc AI loanh quanh, đừng để nó tự "sửa cho xanh" bằng cách nới lỏng test - hãy chuyển sang chế độ gỡ lỗi có phương pháp (xem debug với AI) và soi lại chất lượng thay đổi bằng một vòng AI code review trước khi commit.

Tự động hóa TDD: hooks & skill ép phase gate

Kỷ luật thủ công dễ trôi. Hai cách tự động hoá để phase gate không phụ thuộc trí nhớ của bạn:

Hooks chạy test tự động. Claude Code hỗ trợ hook chạy sau mỗi lần sửa file. Bạn cấu hình hook chạy pytest -q (hoặc npm test, vitest run) sau mỗi edit; nếu test đỏ, hook báo ngay để AI phải sửa trước khi đi tiếp. Đây là cách biến "nhớ chạy test" thành "không thể quên chạy test".

Skill/subagent dựng sẵn quy trình. Thay vì tự viết lại quy ước test-first cho từng dự án, bạn có thể dùng bộ kit đóng gói sẵn.

Tăng tốc bằng kit dựng sẵn: Nếu không muốn tự dựng hook và quy ước từ đầu, bộ kit AgentKit cho Claude Code (giảm 20% qua link) (agentkit.best, CLI ak - khác với OpenAI AgentKit) đi kèm 60+ skill và 30+ workflow cho engineer, gồm cả workflow code review. Bạn có thể ghép chúng với vòng red-green-refactor để có phase gate và review tự động, đỡ phải cấu hình thủ công. Chi tiết trong bài Engineer Kit có gì (review). Engineer Kit hiện có giá $99 (trang không nêu phí định kỳ).

Dù tự dựng hay dùng kit, nguyên tắc không đổi: máy phải là bên chạy test và báo kết quả, không phải AI tự khai.

Khi nào nên & KHÔNG nên TDD với AI

TDD với AI không phải viên đạn bạc. Trung thực về chỗ nó tỏa sáng và chỗ nó cản đường:

Nên dùng khi: logic nghiệp vụ, hàm thuần (input → output rõ), xử lý dữ liệu, API backend, và đặc biệt là sửa bug - viết một test tái hiện bug (đỏ) rồi sửa cho xanh là quy trình bug-fix gọn nhất. Đây cũng là nơi TDD phát huy nhất khi bạn dùng Claude Code viết backend API, vì hợp đồng endpoint rất dễ đóng băng thành test.

Ít hợp khi: giai đoạn exploration/prototype còn đang mò yêu cầu (test viết ra sẽ phải xoá liên tục), UI styling và cảm giác thị giác (test đắt mà không bắt được "đẹp/xấu"), hoặc script dùng một lần. Khi yêu cầu còn mơ hồ, ép test-first chỉ làm chậm - cứ prototype tự do, chốt được hành vi rồi mới quay lại bọc test cho phần đáng giữ.

Câu hỏi thường gặp (FAQ)

TDD với AI có thay thế được TDD thủ công không?

Không thay thế nguyên tắc, chỉ thay đổi người gõ phím. Bạn vẫn phải quyết định test kiểm tra gì và đánh giá test có ý nghĩa không; AI lo phần viết và chạy. Kỷ luật red-green-refactor vẫn là của bạn.

Claude tự viết test có đúng và đủ không?

Thường đúng cho case cơ bản nhưng hay bỏ sót case biên (rỗng, null, số âm, unicode, lỗi mạng). Hãy đọc lại test AI viết và yêu cầu bổ sung case biên trước khi tin. Test là spec, nên spec phải do bạn duyệt.

Dùng framework nào - pytest, Jest hay Vitest?

Framework nào dự án bạn đang dùng thì dùng cái đó; Claude Code thành thạo cả pytest (Python), Jest và Vitest (JS/TS). Điều quan trọng là chỉ định rõ framework trong prompt hoặc CLAUDE.md để AI không tự chọn linh tinh.

Có cần biết viết test trước khi làm TDD với AI không?

Nên biết cơ bản. Bạn không cần viết test giỏi, nhưng phải đủ để đọc hiểu và phán xét test AI viết - nếu không, bạn sẽ ký duyệt cả những test vô nghĩa. Xem TDD như cách vừa làm vừa nâng kỹ năng test của chính mình.

Test do AI viết có đáng tin để chặn regression không?

Đáng tin khi có hai điều kiện: bạn đã duyệt test, và test được chạy thật (không phải AI tự khai pass). Bổ sung tầng lint → unit → e2e để bù chỗ AI hay báo "done" sớm.

Áp dụng TDD với AI cho dự án cũ (legacy) được không?

Được, nhưng khác thứ tự: với legacy, bạn viết "characterization test" khoá hành vi hiện tại trước, rồi mới refactor an toàn. Với tính năng mới thêm vào dự án cũ thì vẫn test-first như bình thường.

Kết luận + bước tiếp theo

TDD với AI rút gọn lại còn một vòng lặp: viết test đỏ → để Claude Code làm xanh → refactor giữ xanh, và luôn bắt máy chạy test thật thay vì tin lời AI. Chìa khoá không phải công cụ mạnh hơn, mà là ép được AI viết test trước bằng prompt tách phase và quy ước CLAUDE.md. Bước tiếp theo: ráp TDD vào một quy trình hoàn chỉnh với bài brainstorm → plan → cook → ship, và củng cố nền tảng quy trình qua vibe coding làm đúng cách.

Muốn Claude Code mạnh hơn ngay? Nếu bạn muốn bỏ qua khâu tự dựng hook, quy ước test-first và workflow code review, bộ kit engineer đóng gói sẵn những mảnh đó để ghép vào vòng red-green-refactor.

Xem giá AgentKit (giảm 20% qua link) →

J

Jasmine

Tác giả · Jasmine Daily

Người viết nên Jasmine Daily - ghi lại những suy nghĩ, trải nghiệm và những khoảnh khắc đời thường. Thật lòng, không vội vàng, không hoàn hảo.

Jasmine Daily

Vẫn còn nhiều điều đang chờ được đọc.

Nếu bài viết này chạm đến bạn, hãy ghé xem thêm vài trang khác trong cuốn nhật ký này.

Đọc tiếp

Bài viết liên quan