Test to Code — Nếu Không Test, Thì Code Của Bạn Không Hoạt Động (Pragmatic Programmer #41)

Mở đầu
Hồi mới code, mình cũng từng có suy nghĩ: "Viết xong tính năng đã, rồi test sau." Nghe quen không mấy bạn? Kết quả là? Bug chồng bug, sửa cái này hỏng cái kia, và câu "test sau" kéo dài mãi chẳng thấy đâu.
Pragmatic Programmer Topic 41 — Test to Code — đập thẳng vào cái mindset sai lầm đó. Ý chính rất đơn giản: nếu mày không test, mày không biết code mày có chạy không. Nói thẳng luôn.
Ảnh: ThisIsEngineering — Pexels
Testing không phải phase riêng
Cái mình thấy hay nhất trong topic này là cách tác giả đặt vấn đề: testing KHÔNG phải một phase trong software development lifecycle. Nó là một phần của coding.
"Code that isn't tested doesn't work. Period."
Nói thiệt, câu này nghe hơi cực đoan, nhưng nghĩ kỹ thì đúng. Nếu bạn viết một function mà không có test nào chứng minh nó chạy đúng, thì làm sao bạn dám chắc? Bằng mắt thường? Bằng "à mình chạy thử thấy OK"? Tin mình đi, mấy cái "chạy thử tay" đó bỏ sót cả rổ bug, nhất là edge cases.
Tác giả khuyến khích viết test cùng lúc với code — thậm chí viết trước khi code (hello TDD). Cách này buộc bạn nghĩ về interface trước, về các trường hợp cần xử lý trước, chứ không phải "code trước rồi nghĩ sau".
Unit test không phải là tất cả
Nhiều bạn nghĩ "có unit test là đủ". Sai. Pragmatic Programmer nhắc nhở phải có cả một hệ thống test:
| Loại test | Mục đích |
|---|---|
| Unit test | Kiểm tra từng module riêng lẻ |
| Integration test | Kiểm tra các module giao tiếp với nhau |
| Regression test | Đảm bảo tính năng cũ không hỏng khi thêm mới |
| Performance test | Code chạy nhanh hay chậm? |
| Usability test | User có dùng được không? (ít người nhớ cái này) |
Cảm giác an toàn thật sự đến khi bạn có tất cả những lớp test này, chứ không chỉ unit test phủ 100% code.
Ảnh: Tom Swinnen — Pexels
Test cho phép bạn tự tin thay đổi
Mình thấy cái lợi lớn nhất của test là sự tự tin. Khi bạn có test coverage tốt, bạn có thể:
- Refactor code cũ mà không sợ hỏng
- Nâng cấp thư viện mà không run
- Thêm tính năng mới biết chắc phần cũ vẫn chạy
Không có test, mỗi lần sửa code là một lần hồi hộp — "liệu cái này có làm hỏng cái kia không?". Nghe như đánh bạc với production vậy.
Cái bẫy "test sau"
Đây là cái bẫy mình thấy rất nhiều bạn (kể cả mình ngày xưa) mắc phải:
- "Hẹn cuối sprint viết test" — cuối sprint deadline dí, test out
- "Tính năng nhỏ quá, test làm gì" — tính năng nhỏ × 10 = refactor đau đầu
- "Test manual cũng được mà" — chạy tay 20 test cases mất 15 phút, chán bỏ
Lời khuyên từ sách: hãy viết test như một phần của công việc, không phải phần thưởng sau khi xong việc. Giống như đánh răng vậy — không ai đánh răng sau khi ăn xong cả ngày đúng không? (Hy vọng là không.)
Automation là chìa khoá
Bất kỳ test nào chạy bằng tay cũng sẽ dần biến mất khỏi quy trình. Mệt, lười, quên — đủ thứ lý do. Automation là giải pháp duy nhất.
Thiết lập CI/CD ngay từ đầu. Mỗi pull request phải chạy test tự động. Nếu test đỏ, không cho merge. Cứng nhắc? Hơi, nhưng nó ép cả team giữ kỷ luật.
Kết
Topic 41 là lời nhắc nhở mạnh mẽ: testing không phải chuyện "sau này" — nó là một phần của coding. Bạn viết code mà không test, thì code đó chưa hoàn thành. Đơn giản vậy thôi.
Mình cũng không nói phải TDD 100% hay test coverage 100% mới được — cái gì cực đoan quá cũng không tốt. Nhưng ít nhất hãy có test cho những logic quan trọng, những đoạn code dễ hỏng, và automation để không ai quên chạy test.
Còn bạn thì sao? Bạn có viết test cùng lúc với code không, hay cũng từng mắc bẫy "test sau"? Hóng bình luận bên dưới nha!
Ảnh: RDNE Stock project — Pexels
📋 Phụ lục thuật ngữ
- Unit test — test từng đơn vị code nhỏ nhất (hàm, method) riêng biệt
- Integration test — test sự phối hợp giữa các module với nhau
- Regression test — test đảm bảo code mới không làm hỏng tính năng cũ
- TDD (Test-Driven Development) — viết test trước, code sau, refactor cuối
- CI/CD — Continuous Integration / Continuous Deployment — tự động build + test + deploy