Viết Unit Test sao cho Clean - Bài học từ Clean Code
Unit Test có cần phải "clean" không?
Bạn đã bao giờ rơi vào tình huống phải sửa một đoạn code và ngay lập tức nhận về hàng loạt lỗi test không? Nhưng khi nhìn vào test case, bạn chẳng hiểu nổi chúng đang kiểm tra cái gì. Nếu có, chào mừng bạn đến với thế giới của unit test kém chất lượng.
Robert C. Martin (Uncle Bob) trong cuốn Clean Code nhấn mạnh rằng unit test cũng cần phải clean như code sản phẩm. Vì sao ư? Vì nếu test code lộn xộn, nó không giúp ta tự tin sửa code mà ngược lại còn gây thêm rắc rối.
Tại sao phải coi Unit Test là "công dân hạng nhất"?
Trong thế giới lập trình, code sản phẩm thường được ưu tiên hàng đầu, còn test code thì bị xem như phần phụ. Nhưng hãy tưởng tượng bạn đang xây một cây cầu, liệu bạn có muốn thử tải trọng sau khi hoàn tất, hay muốn kiểm tra từng bước khi thi công?
Nếu ta viết test cẩu thả, đến một ngày nào đó, khi test thất bại, ta sẽ tốn rất nhiều thời gian để hiểu tại sao nó lại hỏng. Đó là lý do ta cần viết unit test sao cho rõ ràng, dễ đọc và dễ bảo trì.
Ba luật của TDD - Có thực sự cần thiết? Test-Driven Development (TDD) đề xuất ba luật:
- Viết test trước khi viết code.
- Viết đủ code để test chạy pass.
- Viết đủ test để chứng minh code đang hoạt động đúng. Nếu bạn chưa quen với TDD, bạn có thể nghĩ "Thế này mất thời gian quá!". Nhưng thực tế, khi tuân theo quy tắc này, bạn sẽ có một bộ test giúp bạn tự tin thay đổi code mà không lo phá vỡ chức năng cũ.
Làm thế nào để viết Unit Test "sạch"?
1. Viết test dễ đọc, dễ hiểu
Test case là tài liệu sống của dự án. Khi test dễ đọc, chúng giúp ta (và đồng đội) hiểu rõ hệ thống đang hoạt động như thế nào.
Ví dụ:
func TestCalculateDiscount_GoldMember_ShouldApply20PercentDiscount(t *testing.T) {
// Arrange
user := User{Membership: "Gold"}
// Act
discount := CalculateDiscount(user)
// Assert
if discount != 0.2 {
t.Errorf("Expected 20%% discount, got %f", discount)
}
}
2. Mỗi test chỉ kiểm tra một điều
Nếu một test kiểm tra quá nhiều thứ, khi nó thất bại, ta sẽ khó xác định nguyên nhân. Hãy giữ mỗi test ngắn gọn, tập trung vào một hành vi duy nhất.
3. Áp dụng nguyên tắc FIRST
Unit test sạch sẽ tuân theo 5 tiêu chí FIRST:
- Fast - Chạy nhanh.
- Independent - Độc lập, không phụ thuộc test khác.
- Repeatable - Luôn cho kết quả như nhau.
- Self-validating - Tự động kiểm tra đúng/sai.
- Timely - Viết test đúng thời điểm, tốt nhất là trước khi code.
4. Tránh "fragile tests"
Test không nên dễ hỏng chỉ vì ta thay đổi một chút về implementation. Hãy tập trung test kết quả mong muốn, không phải cách thức thực hiện. Ví dụ, thay vì kiểm tra xem hàm có gọi một method nội bộ hay không, hãy kiểm tra đầu vào - đầu ra.
Kết luận
Viết unit test sạch giúp bạn tự tin thay đổi code mà không lo làm hỏng chức năng khác. Nó không chỉ giúp phát hiện lỗi sớm mà còn là tài liệu quan trọng cho cả nhóm. Hãy nhớ: code sạch thì test cũng phải sạch. Nếu một test case khiến bạn mất quá nhiều thời gian để hiểu, có lẽ nó cần được refactor! Bạn có từng gặp trường hợp test code quá rối rắm chưa? Chia sẻ với mình nhé! 🚀
All rights reserved
Bình luận