Tổng quan về unit test với ASP.NET Core, xUnit và Moq

Chào mừng bạn đến với loạt bài viết về kiểm thử đơn vị (unit test) trong ứng dụng ASP.NET Core bằng xUnit và Moq. Và nếu từ cuối cùng nghe có vẻ như vô nghĩa, đừng lo lắng; tất cả sẽ có ý nghĩa sau khi bạn đọc hết loạt bài viết này.

Tại sao bắt đầu viết unit test lại khó đến vậy?

Cách đây khoảng 6 năm, tôi muốn viết unit test để tự động hóa việc kiểm thử ứng dụng mà tôi đang tham gia xây dựng. Đó là những gì một nhà phát triển giỏi làm và tôi mong muốn trở thành một nhà phát triển giỏi.

Nhưng bằng cách nào?

Ứng dụng mà tôi tham gia xây dựng không có bất kỳ bài kiểm tra unit test nào để tự động hóa việc kiểm thử. Chúng tôi phải kiểm tra các chức năng hoàn toàn bằng cơm, đây là một công việc lặp đi lặp lại rất nhàm chán và bỏ sót nhiều bug (QC rất thích điều này, còn tôi thì không).

Tôi muốn thay đổi điều này và đã tìm thấy giải pháp là sử dụng unit test sau khi lang thang trên Google. Tôi đã thử làm theo ví dụ trong các hướng dẫn này và các ví dụ này chạy ngon lành. Tôi nghĩ rằng mình đã tìm thấy kim chỉ nam và suýt nữa đã làm như Acsimet đã từng làm cách đây hơn 2000 năm.

Tuy nhiên khi tôi muốn viết unit test cho dự án thực tế của tôi thì không áp dụng được. Các ví dụ mà tôi làm theo thường rất đơn giản, chẳng hạn như một chương trình cho phép cộng, trừ, nhân, chia hai số nguyên.

Trong khi ứng dụng mà tôi muốn viết unit test khá phức tạp: có giao diện Frontend và Backend, có các API xử lý business ở đằng sau và hầu hết thực hiện các thao tác CRUD (Create, Read, Update, Delete) với cơ sở dữ liệu.

Vấn đề ở đây là tôi cần viết unit test cho cái gì? Vì ứng dụng có rất nhiều thành phần khác nhau, từ UI đến API, business logic tới repository, database, ...

Một vấn đề khác là mọi người luôn nói, "các bài kiểm tra unit test là quan trọng. Bạn phải làm chúng!" và sau đó không bao giờ nói cho tôi biết làm thế nào để viết chúng và làm thế nào để viết chúng thật tốt.

Loạt bài này trình bày một số thứ tôi đã học được và sử dụng trong công việc của tôi. Tôi đã học được rất nhiều về kiểm thử và đặc biệt là kiểm thử đơn vị tự động, và tôi hy vọng rằng một số điều tôi học được sẽ hữu ích cho độc giả của tôi.

Hãy bắt đầu!

Unit test là gì?

Unit test là một loại kiểm thử phần mềm trong đó các đơn vị hoặc thành phần riêng lẻ của một phần mềm được kiểm tra. Mục đích là để xác nhận rằng mỗi đơn vị của mã phần mềm hoạt động như mong đợi.

Unit test được thực hiện bởi các nhà phát triển trong quá trình phát triển (giai đoạn viết code) của một ứng dụng. Unit Test cô lập một đơn vị code và xác minh tính đúng đắn của nó. Một đơn vị có thể là một hàm, một phương thức, một mô-đun hoặc một đối tượng riêng lẻ.

Nếu các bài kiểm tra unit test được viết kỹ lưỡng, nó sẽ giúp các nhà phát triển nắm bắt được những thay đổi ngoài ý muốn gây ra khi sửa đổi mã được kiểm tra. Unit test là chỉ báo đầu tiên cho thấy có điều gì đó không mong muốn đã xảy ra trong mã của chúng ta.

Tại sao nên sử dụng unit test?

Có nhiều lý do giải thích vì sao bạn nên sử dụng unit test. Dưới đây là một trong những lý do đó.

Bởi vì bạn sẽ thường xuyên thay đổi mã của mình. Và khi bạn làm vậy, bạn có thể gây ra một kết quả mà bạn không lường trước được.

Các bài kiểm tra unit test cung cấp cho các nhà phát triển một loại thông tin chi tiết về một thay đổi gây ra một số kết quả không mong muốn. Khi kiểm tra không thành công, chúng tôi có thể kiểm tra xem có vấn đề nào sau đây không:

  • Bản thân bài kiểm tra unit test đã sai, và cần được thay đổi.
  • Kiểm tra dựa trên các phụ thuộc được phân tách không đúng cách.
  • Mã mới thay đổi bị sai và cần được sửa lại.
  • Mã được thay đổi gây ảnh hưởng không mong muốn tới các chức năng khác.

Và đó chỉ là một vài trong số những nguyên nhân có thể xảy ra. Nhưng nếu không có các bài kiểm tra unit test, chúng tôi sẽ không nhận được bất kỳ thông báo nào rằng đã xảy ra sự cố, chúng tôi sẽ tiếp tục đi trên một con đường xấu.

Có nên viết unit test cho mọi thứ không?

Câu trả lời ngắn gọn là KHÔNG.

Không phải tất cả mọi thứ đều cần phải được kiểm tra bằng unit test. Các đoạn mã có thể không bao giờ thay đổi, hoặc không quan trọng đối với chức năng chính của hệ thống, hoặc rất khó để viết unit test hoặc đơn giản là quá lãng phí khi viết unit test, ...

Ngoài ra, chính xác khi nào và tại sao một thứ gì đó cần được viết unit test. Không có một kế hoạch kiểm thử nào phù hợp với tất cả. Mọi hệ thống đều khác nhau, mọi nhu cầu đều khác nhau, và bạn phải đánh giá phạm vi kiểm thử cần thiết dựa trên thiết kế và độ phức tạp của hệ thống bạn đang xây dựng, chưa kể thời gian thực hiện các kiểm tra đó.

Vậy chúng ta nên viết unit test cho cái gì?

Chúng ta nên viết unit test cho bất cứ điều gì là:

  • Quan trọng đối với toàn bộ chức năng của ứng dụng.
  • Đã biết hoặc có khả năng hỏng hóc cao.
  • Có khả năng thay đổi trong tương lai.

Nói cách khác: bạn nên kiểm tra mã nào quan trọng, dễ hỏng hoặc có khả năng thay đổi.

Nói một cách đơn giản và dễ hiểu nhất thì nên viết unit test cho:

  • Các lớp thư viện, lớp hạ tầng dùng chung cho nhiều project.
  • Các lớp tầng business logic.
  • Các lớp controller của Web UI và Web API.

Làm thế nào để chúng tôi viết unit test?

Tôi rất vui vì bạn đã hỏi điều đó. Chính xác thì chúng ta phải viết unit test như thế nào?

Có rất nhiều thư viện cho unit test có sẵn trong thế giới .NET như MSTest, NUnit, XUnit và các thư viện khác.

Đây là những thứ phổ biến nhất và chúng hoạt động theo những cách tương tự nhau. Tuy nhiên, theo ý kiến ​​của tôi xUnit mang lại sự dễ sử dụng và khả năng tái sử dụng tốt nhất, vì vậy nó được chúng tôi sẽ sử dụng trong loạt bài này.

Thông tin cơ bản về XUnit

XUnit là một framework unit test cho .NET cung cấp một cách dễ dàng để viết code, chạy và debug các bài kiểm tra unit test.

Trong xUnit, một bài kiểm tra cơ bản là một phương thức trong lớp công khai, không có tham số hoặc giá trị trả về, được gắn nhãn bằng thuộc tính Fact, như sau:

public class TestCases
{
    [Fact]
    public void ClassName_MethodName_ExpectedResult() 
    {
        // Arrange
        
        // Fact
        
        // Assert
    }
}

Tại thời điểm này, bài kiểm tra trên chính xác là không có gì. Để điền vào những gì bài kiểm tra nên làm, chúng ta có thể làm theo phương pháp "Arrange, Act, Assert."

LƯU Ý: Bạn có thể thiết lập xUnit để chạy các bài kiểm tra với đầu vào và đầu ra bằng thuộc tính [Theory]; hãy xem bài viết "Sử dụng xUnit để kiểm tra mã C# của bạn" để biết một ví dụ tuyệt vời. Chúng ta sẽ tìm hiểu kỹ hơn về vấn đề này trong phần sau của loạt bài này.

Arrange, Act, Assert (AAA)

Cụm từ "Arrange, Act, Assert" hay AAA là một cách hay để ghi nhớ cấu trúc của một bài kiểm tra unit test.

  • Arrange có nghĩa là để thiết lập môi trường thử nghiệm cần thiết và các phụ thuộc. Điều này có thể có nghĩa là tạo một tập hợp dữ liệu thử nghiệm tốt đã biết hoặc tạo mock của một phần phụ thuộc mà chúng ta không muốn kiểm tra. Tóm lại, "Arrange" có nghĩa là "tạo ra những gì bài kiểm tra cần để chạy."
  • Act có nghĩa là thực thi mã cần được kiểm tra, đã được thiết lập trong bước Arrange.
  • Assert có nghĩa là kiểm tra kết quả và đầu ra và xác nhận rằng chúng là những gì chúng ta mong đợi.

Ví dụ dưới đây là một bài kiểm tra unit test có sử dụng mock:

[Fact]
public void PlayerService_GetAllPlayers_InvalidLeague()
{
    //Arrange
    var mockLeagueRepo = new MockLeagueRepository().MockIsValid(false);

    var playerService = new PlayerService(new MockPlayerRepository().Object,
        new MockTeamRepository().Object,
        mockLeagueRepo.Object);

    //Act
    var allPlayers = playerService.GetForLeague(1);

    //Assert
    Assert.Empty(allPlayers);
    mockLeagueRepo.VerifyIsValid(Times.Once());
}

Đừng lo lắng về đoạn mã này; bây giờ, tất cả những gì bạn cần nhớ là mô hình "Arrange, Act, Assert". Chúng ta sẽ xem xét chính xác đoạn mã này làm gì trong phần tiếp theo của loạt bài này.

Ở phần tiếp theo, chúng tôi sẽ trình bày cách sử dụng "mock" cho phép chúng ta dễ dàng tạo và thiết lập các lớp giả lập để thử nghiệm (cũng như tìm hiểu chính xác "mock" là gì).

Sử dụng Moq để viết unit test trong ASP.NET Core
Trong bài viết này, chúng ta sẽ tạo các lớp fluent mock cho các bài kiểm tra unit test bằng cách sử dụng Moq, xUnit và ASP.NET Core.
Unit TestXUnitLập Trình C#.NET CoreASP.NET CoreMockMoq
Bài Viết Liên Quan:
Tạo DataAttribute tùy chỉnh cho Theory của xUnit để tải dữ liệu từ file JSON
Trung Nguyen 22/04/2021
Tạo DataAttribute tùy chỉnh cho Theory của xUnit để tải dữ liệu từ file JSON

Trong bài viết này hướng dẫn bạn cách tạo lớp DataAttribute tùy chỉnh để tải dữ liệu cho bài kiểm tra unit test viết bằng xUnit của bạn.

Tạo các bài kiểm tra unit test được tham số hóa trong xUnit với Theory, InlineData, ClassData và MemberData
Trung Nguyen 18/04/2021
Tạo các bài kiểm tra unit test được tham số hóa trong xUnit với Theory, InlineData, ClassData và MemberData

Trong bài viết này, tôi sẽ hướng dẫn bạn tạo các bài kiểm tra unit test được tham số hóa trong xUnit với Theory, InlineData, ClassData và MemberData

Sử dụng Theory và InlineData của xUnit để viết unit test
Trung Nguyen 18/04/2021
Sử dụng Theory và InlineData của xUnit để viết unit test

Trong bài này, chúng ta sẽ sử dụng thuộc tính [Theory] và [InlineData] của xUnit để nhanh chóng viết một loạt các bài kiểm tra unit test.

Unit test cho controller trong ASP.NET Core với xUnit và Moq
Trung Nguyen 18/04/2021
Unit test cho controller trong ASP.NET Core với xUnit và Moq

Trong bài viết này, chúng ta sẽ viết một số bài kiểm tra unit test cho Controller của ứng dụng ASP.NET sử dụng xUnit và Moq.