
Các nguyên tắc viết Unit Test hiệu quả trong .NET
May 25, 2025Trước đó mình có 3 bài viết về Unit test bạn có thể tìm đọc lại
Unit test, chuyện chẳng của riêng devs nào!
Bài viết này mình sẽ đi sâu hơn về Unit test khi làm việc với dự án .Net C#.

Việc viết Unit Test mang lại rất nhiều lợi ích cho các dự án .NET Core hoặc .NET Standard. Không chỉ hỗ trợ kiểm tra hồi quy (regression testing), Unit Test còn đóng vai trò như một dạng tài liệu thực thi, đồng thời thúc đẩy thiết kế tốt hơn. Tuy nhiên, nếu viết không đúng cách, Unit Test có thể trở thành gánh nặng và làm tăng độ phức tạp trong hệ thống.
Dưới đây là tập hợp các best practices giúp bạn viết Unit Test dễ đọc, dễ bảo trì và đáng tin cậy hơn.
Lợi ích của việc viết Unit Test
- Tiết kiệm thời gian kiểm thử thủ công: Functional test thường tốn nhiều thời gian, cần thực hiện nhiều thao tác phức tạp. Trong khi đó, Unit Test có thể chạy chỉ trong vài milliseconds, dễ dàng thực hiện với chỉ một cú click.
- Ngăn ngừa lỗi hồi quy: Mỗi lần build, bạn có thể chạy lại toàn bộ test suite để đảm bảo các thay đổi mới không phá vỡ tính năng cũ.
- Tài liệu thực thi: Một test được đặt tên tốt sẽ cho biết rõ đầu vào – đầu ra mong đợi. Đây là cách tuyệt vời để hiểu một method hoạt động như thế nào trong các điều kiện khác nhau.
- Giảm coupling: Việc viết Unit Test giúp phát hiện code bị coupling cao và thúc đẩy bạn thiết kế theo hướng loosely coupled để dễ test hơn.
Đặc điểm của một Unit Test tốt
- Nhanh: Mỗi test nên hoàn thành trong milliseconds.
- Isolated: Không phụ thuộc vào file system, database hay các yếu tố ngoài control.
- Repeatable: Kết quả test phải luôn giống nhau nếu không có thay đổi nào.
- Self-checking: Không cần kiểm tra thủ công — test phải tự xác nhận pass/fail.
- Timely: Không nên mất nhiều thời gian viết test hơn so với phần logic được test.
Code coverage và chất lượng mã nguồn
Code coverage cao thường được xem như là dấu hiệu của chất lượng mã tốt. Tuy nhiên, đừng quá phụ thuộc vào số % coverage. Mục tiêu nên là viết các test có giá trị, thay vì cố gắng test mọi nhánh logic mà không mang lại nhiều ý nghĩa.
Thuật ngữ thường dùng trong Unit Test
- Fake: Là khái niệm tổng quát, có thể là mock hoặc stub tuỳ theo cách sử dụng.
- Stub: Là phiên bản thay thế có kiểm soát cho dependency gốc, thường không tham gia vào quá trình Assert.
- Mock: Tương tự stub, nhưng dùng trong Assert để kiểm tra hành vi đã xảy ra.
Best Practices
1. Tránh phụ thuộc vào hạ tầng
Unit Test nên tránh tương tác với database, filesystem hoặc external service. Hãy tận dụng Dependency Injection để dễ mock, và phân tách rõ giữa Unit Test và Integration Test bằng cách tổ chức thành các project riêng biệt.
2. Đặt tên test có ý nghĩa
Tên test nên gồm 3 phần:
- Tên method được test
- Tình huống xảy ra
- Kết quả mong đợi
Ví dụ:
[Fact]
public void Add_SingleNumber_ReturnsSameNumber()
3. Áp dụng Arrange – Act – Assert
Mỗi test nên chia rõ ba phần:
- Arrange: Setup object, dữ liệu
- Act: Gọi method cần test
- Assert: Kiểm tra kết quả
4. Viết test với dữ liệu tối giản
Chỉ cung cấp những input tối thiểu cần thiết để test đúng hành vi cần kiểm tra. Tránh thêm các giá trị không cần thiết.
5. Tránh magic strings
Sử dụng const để khai báo các chuỗi cố định thay vì hardcode trực tiếp trong test, điều này giúp test dễ đọc và dễ bảo trì hơn.
6. Tránh logic phức tạp trong test
Không nên sử dụng vòng lặp, điều kiện rẽ nhánh trong test. Nếu cần test nhiều trường hợp, hãy dùng [Theory]
và [InlineData]
.
7. Sử dụng helper method thay vì Setup/Teardown
Trong xUnit, nên dùng constructor hoặc helper methods để khởi tạo object. Tránh Setup/Teardown để tránh chia sẻ state giữa các test.
8. Tránh nhiều hành động (Act) trong một test
Một test nên tập trung kiểm tra duy nhất một hành vi. Nếu cần kiểm tra nhiều hành động, hãy tách thành nhiều test.
9. Không test trực tiếp private method
Chỉ nên test thông qua public method. Private method là chi tiết triển khai, không nên được test riêng lẻ.
10. Xử lý static reference bằng cách tạo “seam”
Ví dụ: thay vì gọi DateTime.Now
trực tiếp, hãy inject một IDateTimeProvider
để có thể mock thời gian trong test.
public interface IDateTimeProvider
{
DayOfWeek GetCurrentDay();
}
Từ đó bạn có thể mock giá trị và kiểm tra logic theo ngày tùy ý.
Việc tuân thủ các nguyên tắc trên không chỉ giúp tăng độ tin cậy của test suite mà còn đảm bảo rằng team có thể mở rộng, refactor hệ thống dễ dàng mà không lo phá vỡ tính năng cũ. Khi test của bạn tốt, bạn có thể tin tưởng vào chúng như một dạng kiểm soát chất lượng tự động, và đó là một lợi thế rất lớn trong mọi dự án phần mềm.
Bạn có thể đọc chi tiết hơn tại đây: https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices