Bài viết này sẽ hướng dẫn bạn tạo các bài kiểm tra tự động với xUnit cho các ứng dụng C# của bạn. Bạn sẽ học những kiến thức cơ bản về tự động hóa kiểm thử và cách tạo kiểm thử đơn vị (unit test) và kiểm thử tích hợp (integration test).
Kiểm thử đảm bảo rằng mã ứng dụng của bạn đang làm đúng những gì nó phải làm. Mã ứng dụng có thể là những câu lệnh tầm thường, nhưng đôi khi những câu lệnh này bị đánh giá thấp, đặc biệt là khi bạn thay đổi codebase hiện có của mình.
Bạn phải đảm bảo rằng các thay đổi của bạn không chỉ hoạt động như dự định mà còn phải đảm bảo rằng những đoạn mã khác tiếp tục thực hiện công việc mong đợi của nó.
Kiểm tra thủ công là một công việc rất khó khăn, không chỉ để thực hiện các bài kiểm tra mà còn vì bạn phải thực hiện chúng rất nhiều lần. Đó là một công việc lặp đi lặp lại và khi có một công việc lặp đi lặp lại, bạn cần tự động hóa.
Theo truyền thống, có một số loại kiểm thử tự động khác nhau. Hãy cùng xem nhanh các định nghĩa của những cái phổ biến nhất:
Nhiều định nghĩa kiểm thử khác tồn tại dựa trên các mục tiêu thử nghiệm và quan điểm mà bạn nhìn vào chúng. Nhưng những cái trên đại diện cho những cái phổ biến nhất theo quan điểm của nhà phát triển.
Tất nhiên, mỗi loại kiểm thử mang lại giá trị để đảm bảo tính đúng đắn của ứng dụng phần mềm, và mỗi loại đều có điểm mạnh và điểm yếu. Ví dụ, trong khi các bài kiểm tra đơn vị thường được thực hiện rất nhanh, các bài kiểm tra E2E lại chậm hơn và có thể có nhiều điểm lỗi khác nhau do sự tương tác của nhiều hệ thống.
Khi kiểm tra hệ thống của mình, bạn không thể giả vờ có thể bao quát tất cả các trường hợp sử dụng có thể xảy ra. Bạn nên giới hạn chúng trong một tập hợp con do sự gia tăng độ phức tạp khi chuyển từ một đơn vị đơn giản sang một thành phần của hệ thống, một phần là do thời gian cần thiết để thực hiện các bài kiểm tra.
Thông thường, số lượng bài kiểm tra giảm trong khi chuyển từ bài kiểm tra đơn vị đến bài kiểm tra E2E, theo sơ đồ Kim tự tháp kiểm tra nổi tiếng:
Về cách cấu trúc các bài kiểm tra tự động của bạn, một cách tiếp cận điển hình tuân theo cái gọi là mẫu AAA. Tên bắt nguồn từ chữ cái đầu của ba hành động thường cần để thực hiện kiểm thử:
Trong suốt bài viết này, bạn sẽ sử dụng mẫu này để viết các bài kiểm tra của mình.
Nền tảng .NET Core hỗ trợ các framework kiểm thử khác nhau. Tuy nhiên, xUnit đã trở nên phổ biến nhất do tính đơn giản, mạnh mẽ và khả năng mở rộng của nó.
Dự án được hỗ trợ bởi .NET Foundation và là một phần của các phiên bản .NET Core mới hơn. Điều này có nghĩa là bạn không cần cài đặt bất cứ thứ gì ngoài .NET Core SDK.
Để hiểu cách sử dụng xUnit để tự động hóa các bài kiểm tra của bạn, hãy cùng khám phá những điều cơ bản bằng cách tạo các bài kiểm tra đơn vị cho một dự án hiện có.
Bạn có thể đã nghe nói về Phát triển theo hướng kiểm thử (TDD - Test-Driven Development). Nó là một quá trình phát triển phần mềm nhằm thúc đẩy việc viết các bài kiểm tra trước khi viết mã ứng dụng của bạn. Cách tiếp cận này dẫn đến một chu kỳ phát triển ngắn và lặp đi lặp lại dựa trên việc viết một bài kiểm tra và để nó không thành công, sửa lỗi bằng cách viết mã ứng dụng và cấu trúc lại mã ứng dụng để dễ đọc và hiệu suất cao.
Bài viết này sẽ hướng dẫn bạn viết các bài kiểm tra mà không đề cập bất kỳ cách tiếp cận cụ thể nào để phát triển phần mềm.
Để minh họa tạo unit test sử dụng xUnit, chúng ta sẽ tạo một project .NET Core Console tên là Validator.Password
. Project này là một thư viện rất đơn giản để xác thực mật khẩu với những ràng buộc sau:
Việc triển khai nó dựa trên lớp sau được định nghĩa trong tập tin PasswordValidator.cs
:
using System.Text.RegularExpressions;
namespace Validators.Password
{
public class PasswordValidator
{
public bool IsValid(string password)
{
Regex passwordPolicyExpression = new Regex(@"((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#!$%]).{8,20})");
return passwordPolicyExpression.IsMatch(password);
}
}
}
Như bạn có thể thấy, logic xác thực được thực hiện bởi phương thức IsValid()
thông qua một biểu thức chính quy. Ở đây, lớp PasswordValidator
đại diện cho một đơn vị mã vì nó khép kín và tập trung vào một mục tiêu cụ thể.
Để đảm bảo rằng phương thức IsValid()
đang hoạt động như bạn mong đợi, bạn cần tạo một project để kiểm thử.
Đảm bảo bạn đang ở trong thư mục project, gõ các lệnh sau trong cửa sổ dòng lệnh:
dotnet new xunit -o PasswordValidator.Tests
dotnet add ./PasswordValidator.Tests/PasswordValidator.Tests.csproj reference ./PasswordValidator/PasswordValidator.csproj
Lệnh đầu tiên tạo project unit test, trong khi lệnh thứ hai thêm vào đó một tham chiếu đến dự án PasswordValidator
.
Hoặc đơn giản nhất là trong Visual Studio bạn thêm project mới vào dự án với mẫu project như sau:
Sau khi tạo project kiểm thử bằng xUnit xong, bạn add project Validator.Password
vào project này.
Bây giờ chúng ta sẽ đổi tên tập tin PasswordValidator.Tests/UnitTest1.cs
thành PasswordValidator.Tests/ValidityTests.cs
và thay thế nội dung của tập tin như sau:
using System;
using Xunit;
using Validators.Password;
namespace PasswordValidatorTests
{
public class ValidityTest
{
[Fact]
public void ValidPassword()
{
//Arrange
var passwordValidator = new PasswordValidator();
const string password = "Th1sIsapassword!";
//Act
bool isValid = passwordValidator.IsValid(password);
//Assert
Assert.True(isValid, $"The password {password} is not valid");
}
}
}
Ở đây bạn sẽ thấy lớp ValidityTest
đang chứa bài kiểm tra unit test cho phương thức IsValid()
của lớp PasswordValidator
.
Bài kiểm tra unit test duy nhất hiện được triển khai là phương thức ValidPassword()
. Phương thức này có attribute Fact
, cho xUnit biết rằng đây là một bài kiểm tra.
Các câu lệnh trong phần thân của phương thức ValidPassword()
được sắp xếp để làm nổi bật mẫu AAA được đề cập ở trên.
Trong bước Arrange, bạn tạo một thể hiện của lớp PasswordValidator
và định nghĩa một mật khẩu hợp lệ.
Trong bước Act, bạn gọi phương thức IsValid()
với mật khẩu đã định nghĩa trước đó.
Cuối cùng, bước Assert xác minh rằng kết quả trả về là kết quả mong đợi. Kiểm tra này sử dụng đối tượng Assert
, đối tượng này cung cấp nhiều phương thức để xác minh kết quả.
Trong trường hợp này, bạn đang sử dụng phương thức True()
, phương thức này thành công khi đối số đầu tiên của nó là true. Nếu không, kiểm tra không thành công và hiển thị thông điệp được cung cấp dưới dạng đối số thứ hai.
Để chạy unit test đầu tiên này, hãy đảm bảo bạn đang ở trong thư mục của project PasswordValidator.Tests
và nhập lệnh sau vào cửa sổ đầu cuối của bạn:
dotnet test
Hoặc trong Visual Studio bạn click chuột phải vào project PasswordValidator.Tests
rồi chọn Run Tests. Bạn sẽ thấy một cái gì đó tương tự như sau trong bảng điều khiển của bạn:
Microsoft (R) Test Execution Command Line Tool Version 16.3.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 1.1692 Seconds
Xin chúc mừng, bài kiểm tra unit test đầu tiên của bạn đã vượt qua!
Tips: để xem nhanh tất cả các bài kiểm tra unit test và kết quả chạy các bài kiểm tra này trong Visual Studio bạn truy cập menu View -> Test Explorer (hoặc nhấn Ctrl + E, T).
Khi bạn đang kiểm tra mã của mình, bạn không nên chỉ xác minh các trường hợp thành công; có nghĩa là những trường hợp mà mọi thứ vẫn ổn.
Bạn cũng cần phải xác minh các trường hợp thất bại. Đối với phương thức IsValid()
, bạn phải xác minh một trường hợp có thể xảy ra khi mật khẩu được truyền dưới dạng đối số không tuân thủ các ràng buộc.
Vì vậy, hãy thêm vào bài kiểm tra unit test mới đó là thêm phương thức NotValidPassoword()
vào lớp ValidityTest
, như được hiển thị bên dưới:
using System;
using Xunit;
using Validators.Password;
namespace PasswordValidatorTests
{
public class ValidityTest
{
[Fact]
public void ValidPassword()
{
// ...code...
}
[Fact]
public void NotValidPassword()
{
//Arrange
var passwordValidator = new PasswordValidator();
const string password = "thisIsaPassword";
//Act
bool isValid = passwordValidator.IsValid(password);
//Assert
Assert.False(isValid, $"The password {password} should not be valid!");
}
}
}
Trong trường hợp này, bạn truyền một mật khẩu không hợp lệ và trong bước Assert, bạn sẽ sử dụng phương thức False để chỉ định rằng giá trị được phương thức IsValid()
trả về là false
. Nếu bạn chạy lại các bài kiểm tra unit test, bạn sẽ nhận được hai bài kiểm tra thành công.
Hai trường hợp về tính hợp lệ của mật khẩu được kiểm tra bởi các bài kiểm tra unit test ở trên là không đầy đủ.
Chúng chỉ là hai ví dụ đơn giản về các trường hợp thành công và thất bại, nhưng tất nhiên, các trường hợp có thể để kiểm tra còn nhiều hơn thế nữa.
Bạn không thể mong đợi có thể kiểm tra hết mọi trường hợp có thể xảy ra, nhưng bạn có thể kiểm tra một tập hợp con đáng kể các trường hợp điển hình.
Điều này giúp bạn có phạm vi mã được kiểm tra lớn hơn cho mã sản xuất của bạn. Trong ví dụ xác thực mật khẩu, điều này có nghĩa là bạn nên xác định một tập mật khẩu hợp lệ và không hợp lệ đại diện. Đối với mỗi mật khẩu trong các tập này, bạn nên áp dụng một trong các bài kiểm tra được triển khai ở trên.
Cách tiếp cận này sẽ đảm bảo sự tin cậy đáng kể vào hoạt động chính xác của phương thức IsValid()
. Nhưng nó yêu cầu phải sao chép cùng một mã cho mỗi mật khẩu mẫu để kiểm tra. Bạn biết rằng sao chép mã không phải là một best practice.
May mắn thay, xUnit có thể giúp bạn vấn đề này bằng cách sử dụng attribute Theory. Theory là một bài kiểm tra unit test có tham số cho phép bạn thực hiện một tập hợp các bài kiểm tra đơn vị có cùng cấu trúc.
Theory cho phép bạn triển khai cái được gọi là kiểm thử theo hướng dữ liệu, là một phương pháp kiểm thử dựa nhiều vào sự biến đổi dữ liệu đầu vào.
Vì vậy, để dễ hình dung Theory là gì, hãy thay thế nội dung của tập tin ValidityTests.cs
bằng nội dung sau:
using System;
using Xunit;
using Validators.Password;
namespace PasswordValidatorTests
{
public class ValidityTest
{
[Theory]
[InlineData("Th1sIsapassword!")]
[InlineData("thisIsapassword123!")]
[InlineData("Abc$123456")]
public void ValidPassword(string password)
{
//Arrange
var passwordValidator = new PasswordValidator();
//Act
bool isValid = passwordValidator.IsValid(password);
//Assert
Assert.True(expectedResult);
}
[Theory]
[InlineData("Th1s!")]
[InlineData("thisIsAPassword")]
[InlineData("thisisapassword#")]
[InlineData("THISISAPASSWORD123!")]
[InlineData("")]
public void NotValidPassword(string password)
{
//Arrange
var passwordValidator = new PasswordValidator();
//Act
bool isValid = passwordValidator.IsValid(password);
//Assert
Assert.False(expectedResult);
}
}
}
Đoạn mã trên thay thế attribute Fact
bằng attribute Theory
, bổ sung thêm các attribute InlineData
và tham số password
vào 2 bài kiểm tra unit test.
Như bạn có thể thấy, thay vì hard code thông tin mật khẩu thì ở ví dụ trên, chúng ta tạo tham số password
để lấy thông tin mật khẩu từ attribute InlineData
. Mỗi attribute InlineData
sẽ đại diện cho một mật khẩu trong tập mật khẩu chúng ta cần kiểm tra.
Nói cách khác, mỗi attribute InlineData
đại diện cho một lệnh gọi của bài kiểm tra. Trên thực tế, nếu bạn khởi chạy lệnh, bạn sẽ nhận được thông báo rằng tất cả 3 bài kiểm tra ValidPassword
và 5 bài kiểm tra NotValidPassword
đều thành công.
Bên cạnh attributeInlineData
, xUnit cung cấp cho bạn các cách khác để định nghĩa dữ liệu cho Theory, chẳng hạn như nguồn dữ liệuClassData
là một lớp triển khai interfaceIEnumerable
và nguồn dữ liệuMemberData
là một thuộc tính hoặc một phương thức.
Bạn có thể vui lòng tắt trình chặn quảng cáo ❤️ để hỗ trợ chúng tôi duy trì hoạt động của trang web.
Trong bài viết này, chúng ta sẽ tìm hiểu cách sử dụng thư viện Playwright kết hợp với XUnit để kiểm tra các ứng dụng web ASP.NET Core như người dùng có thể.
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.
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
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.