test-driven-development
bởi obraQuy trình test-driven-development nghiêm ngặt để luôn viết test trước, tuân thủ vòng lặp red-green-refactor và tránh các anti-pattern trong kiểm thử trên dự án thực tế.
Tổng quan
Kỹ năng này làm gì
Kỹ năng test-driven-development mã hóa một quy trình Test-Driven Development (TDD) nghiêm ngặt: bạn luôn viết một test đang fail trước khi viết bất kỳ code production nào, sau đó áp dụng một vòng lặp Red-Green-Refactor có kỷ luật.
Thay vì kiểu hướng dẫn lỏng lẻo “sẽ viết thêm test sau”, kỹ năng này đưa ra những quy tắc cứng:
- Không được viết code production nếu chưa có test đang fail
- Luôn kiểm tra test fail vì đúng lý do
- Chỉ viết lượng code tối thiểu để làm test pass
- Refactor sau khi mọi thứ đã xanh
Kỹ năng này cũng liên kết tới tài liệu testing-anti-patterns chuyên dụng giúp bạn tránh các test mong manh, gây hiểu lầm.
Dành cho ai
Hãy dùng kỹ năng test-driven-development nếu bạn là:
- Lập trình viên muốn có một quy trình tin cậy cho tính năng mới, sửa lỗi và refactor
- Nhóm phát triển đang tiêu chuẩn hóa TDD và cần các quy tắc rõ ràng để mọi người cùng làm theo
- Coach hoặc tech lead muốn rèn luyện thói quen tự động hóa kiểm thử nhất quán trong đội
Kỹ năng này đặc biệt hữu ích trong codebase JavaScript và TypeScript, nhưng các nguyên tắc có thể áp dụng cho mọi ngôn ngữ và framework test.
Các vấn đề kỹ năng này giải quyết
Kỹ năng này được thiết kế để xử lý các vấn đề thường gặp trong phát triển hằng ngày:
- Viết code trước rồi mới thêm test, hoặc không bao giờ thêm
- Không rõ test có thực sự kiểm tra đúng hành vi hay không
- Ngại refactor vì test yếu hoặc thiếu test
- Lạm dụng hoặc dùng sai mocks, kiểm tra hành vi giả thay vì hành vi thật
Bằng cách tuân theo quy trình test-driven-development, bạn sẽ:
- Nhận phản hồi nhanh từ các test đang fail
- Giữ được bản ghi rõ ràng về hành vi mong đợi
- Khiến việc refactor an toàn hơn vì hành vi đã được test bảo vệ
- Tránh các test dễ vỡ, chỉ kiểm tra mocks hoặc chi tiết triển khai
Khi nào nên dùng (và khi nào không)
Kỹ năng gốc khuyến nghị dùng TDD cho:
- Tính năng mới
- Sửa lỗi (bug fixes)
- Công việc refactor
- Bất kỳ thay đổi hành vi nào
Và nêu rõ một số ngoại lệ nên được thảo luận với cộng sự là con người, chẳng hạn:
- Prototype dùng xong bỏ
- Code sinh tự động
- File cấu hình
Nếu bạn chỉ đang “khảo sát thử” hoặc làm một spike nhanh để rồi bỏ đi, kỹ năng test-driven-development nghiêm ngặt này có thể sẽ nặng hơn nhu cầu của bạn. Nhưng với các tính năng cho môi trường production, nó được thiết kế để trở thành quy trình mặc định, không phải một lựa chọn thêm.
Cách sử dụng
Cài đặt
Để cài đặt kỹ năng test-driven-development từ repository obra/superpowers, chạy:
npx skills add https://github.com/obra/superpowers --skill test-driven-development
Lệnh này sẽ kéo definition của kỹ năng test-driven-development và các file liên quan về môi trường Agent Skills của bạn. Sau khi cài đặt, bạn có thể mở các file của kỹ năng để xem các quy tắc chi tiết và ví dụ về anti-pattern.
Các file chính được kỹ năng này tham chiếu gồm:
SKILL.md– bộ quy tắc TDD cốt lõi, bao gồm Iron Law và Red-Green-Refactortesting-anti-patterns.md– hướng dẫn tập trung vào những điều không nên làm trong test
Nguyên tắc cốt lõi: Iron Law
Trung tâm của kỹ năng này là thứ được gọi là Iron Law:
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
Hiểu thực tế là:
- Nếu bạn đã viết code production trước khi có test, bạn phải xóa đoạn code đó và làm lại từ đầu
- Bạn không được giữ lại làm “tham chiếu” hay chỉnh sửa nó trong lúc viết test
- Bạn không được lén xem lại code đã xóa khi hiện thực lại dựa trên test
Quy tắc cứng rắn này đảm bảo mọi dòng code production đều được dẫn dắt bởi một test thực sự.
Quy trình TDD từng bước
Sau khi cài đặt kỹ năng test-driven-development, hãy áp dụng nó như quy trình mặc định:
1. Viết một test đang fail (RED)
- Xác định một hành vi duy nhất (tính năng, bugfix hoặc kết quả refactor)
- Viết một test mới thể hiện hành vi mong đợi
- Chạy test và xác nhận test mới này đang fail
Bạn chưa hoàn thành bước RED cho tới khi:
- Thấy test fail
- Xác nhận nó fail vì đúng lý do (đúng assertion, đúng lỗi)
Nếu kiểu fail sai (ví dụ test crash hoặc fail ở phần setup), hãy sửa test và chạy lại cho đến khi nó fail theo cách có ý nghĩa.
2. Thêm lượng code tối thiểu để pass (GREEN)
- Chỉ hiện thực lượng code đơn giản nhất có thể để làm test đang fail pass
- Tránh thiết kế sớm, abstraction sớm hoặc thêm logic thừa
- Chạy toàn bộ test suite và đảm bảo test mới đã xanh, các test hiện có vẫn pass
Hướng dẫn của kỹ năng nhấn mạnh bước này phải được thu hẹp có chủ đích: mục tiêu của bạn không phải “hoàn thiện tính năng” mà là thoả mãn test.
3. Refactor an toàn (REFACTOR)
Khi mọi thứ đều xanh:
- Dọn dẹp code production: loại bỏ lặp lại, đơn giản hóa logic, đặt tên rõ ràng hơn
- Dọn dẹp test: làm test dễ đọc hơn, tách helper nếu cần
- Liên tục chạy test để đảm bảo hành vi không thay đổi
Bằng cách đó, bạn giữ nguyên hành vi trong khi cải thiện thiết kế, với test suite làm chỗ dựa.
4. Lặp lại vòng lặp
Với mỗi hành vi nhỏ hoặc edge case:
- Thêm một test nữa (RED)
- Làm cho test pass (GREEN)
- Refactor nếu cần (REFACTOR)
Nhiệm vụ của kỹ năng test-driven-development là giữ bạn trong vòng lặp này và khiến việc “nhảy cóc” các bước trở nên khó bào chữa.
Tích hợp với stack của bạn
Các ví dụ trong repository tập trung vào JavaScript và TypeScript, nhưng bạn có thể áp dụng các quy tắc TDD bất kể stack nào:
-
Trong dự án JavaScript/TypeScript (ví dụ dùng Jest, Vitest, Mocha):
- Luôn tạo hoặc chỉnh sửa test (ví dụ trong
*.test.tshoặc*.spec.ts) trước khi sửa file production - Dùng vòng lặp Red-Green-Refactor cho mọi thay đổi với application code
- Luôn tạo hoặc chỉnh sửa test (ví dụ trong
-
Với các ngôn ngữ khác (ví dụ Python, Java, Go, C#):
- Ánh xạ các bước tương tự sang framework test của bạn (pytest, JUnit, Go test, xUnit, v.v.)
- Quy tắc nghiêm “no production code without a failing test” vẫn giữ nguyên
Bản thân kỹ năng không buộc bạn dùng framework cụ thể; nó mã hóa quy tắc quy trình, không phải dependency thư viện.
Sử dụng hướng dẫn testing anti-patterns
File testing-anti-patterns.md đi kèm đóng vai trò tài liệu bổ trợ cho kỹ năng test-driven-development. Hãy mở hoặc tham chiếu tới nó bất cứ khi nào bạn:
- Đang viết hoặc chỉnh sửa test
- Thêm mocks hoặc stubs
- Định thêm các hook chỉ phục vụ test vào class hoặc module production
Một số nguyên tắc chính từ tài liệu này:
- Không test hành vi của mock – test phải kiểm tra hành vi thật, không chỉ xem mock có tồn tại
- Không thêm method chỉ dành cho test vào class production – giữ API production sạch sẽ
- Không mock khi chưa hiểu rõ dependency – over-mocking tạo ra cảm giác tự tin giả
Các ví dụ anti-pattern dùng test phong cách TypeScript để minh họa test tệ vs test tốt, bạn có thể chuyển hóa tương tự cho framework test của mình.
Điều chỉnh quy trình cho đội của bạn
Sau khi cài đặt, bạn có thể điều chỉnh các quy tắc của kỹ năng test-driven-development cho phù hợp thực tế của đội, nhưng vẫn giữ nguyên ý định cốt lõi:
- Thống nhất loại thay đổi nào bắt buộc phải theo TDD nghiêm ngặt (ví dụ toàn bộ module production, logic domain, luồng nghiệp vụ quan trọng)
- Thỏa thuận một vài trường hợp ngoại lệ (ví dụ spike nhanh, code sinh tự động), nhưng phải ghi nhận rõ ràng
- Đưa Iron Law vào checklist code review:
- “Test đang fail nào đã dẫn dắt thay đổi này?”
- “Test đó có được viết trước không?”
Cách này giúp bạn dùng kỹ năng như một chuẩn mực của đội, chứ không chỉ là thói quen cá nhân.
Câu hỏi thường gặp (FAQ)
Kỹ năng test-driven-development có phụ thuộc framework cụ thể không?
Không. Kỹ năng test-driven-development định nghĩa quy trình và quy tắc, không gắn với framework test cụ thể. Các ví dụ trong repository thiên về JavaScript và TypeScript, nhưng bạn có thể áp dụng vòng lặp Red-Green-Refactor tương tự trong bất kỳ ngôn ngữ nào.
Tôi có thực sự phải xóa code đã viết mà không có test không?
Theo Iron Law trong SKILL.md là có. Nếu bạn viết code production trước khi có test đang fail, quy tắc là xóa code đó và làm lại từ test. Ý tưởng là loại bỏ mọi cám dỗ coi test là chuyện làm sau.
Khi nào kỹ năng này không phù hợp?
Hướng dẫn gốc liệt kê một vài ngoại lệ tiềm năng, như prototype dùng rồi bỏ, code sinh tự động hoặc file cấu hình. Với các spike thử nghiệm sống ngắn, test-driven-development nghiêm ngặt có thể nặng nề hơn mức cần thiết. Nhưng với tính năng production, bugfix và refactor, kỹ năng này được thiết kế để trở thành lựa chọn mặc định.
Kỹ năng này giúp gì cho debugging và refactor?
Khi sửa bug hoặc refactor:
- Trước hết, hãy viết một test đang fail tái hiện bug hoặc mô tả hành vi mong muốn
- Sau đó chỉnh code cho tới khi test pass
- Cuối cùng, refactor một cách tự tin vì test đang bảo vệ hành vi
Cách này khiến debugging có hệ thống hơn và giúp refactor an toàn hơn.
Tôi có thể dùng kỹ năng này với code legacy hiện không có test không?
Có, nhưng thường bạn sẽ áp dụng từng bước một:
- Khi đụng tới vùng legacy, trước tiên hãy thêm test ghi lại hành vi hiện tại hoặc hành vi mong muốn
- Xem test fail (nếu bạn định thay đổi hành vi), rồi làm cho nó pass
- Dùng vòng lặp Red-Green-Refactor để dần đưa phần code đó vào vùng có test bảo vệ
Theo thời gian, ngày càng nhiều code legacy của bạn được bảo vệ bởi test.
Sau khi cài đặt thì nên bắt đầu từ đâu?
Sau khi chạy lệnh cài đặt, hãy mở:
SKILL.mdđể xem các quy tắc test-driven-development cốt lõi và mô tả Red-Green-Refactortesting-anti-patterns.mdđể xem các lỗi thường gặp cần tránh khi viết test
Hãy dùng hai file này như tài liệu tham chiếu cho quy trình phát triển hằng ngày của bạn.
