Unit testing là một quá trình phổ biến trong lập trình, bạn cần lập trình sao cho có thể tách ứng dụng mình thành các phần nhỏ để có thể kiểm thử chức năng của những phần này trong nhiều trường hợp khác nhau.

Chi tiết về khái niệm unit testing thì các bạn có thể đọc thêm các bài viết có sẵn rất hay sau đây: unit test là gìvai trò của unit testing.

Trong bài này tôi sẽ chia sẻ cách viết unit test một ứng dụng C# (Khóa học C# tại Techmaster) đơn giản bằng test framework xUnit, tập trung vào giới thiệu cách sử dụng .NET Core CLI và Visual Studio Code.

Tạo 1 .NET Core solution

Khi các bạn làm quen lần đầu, để tạo 1 ứng dụng HelloWorld ai cũng từng chạy câu lệnh `dotnet new console -o HelloWorld`. Câu lệnh trước sẽ tạo một ứng project dạng console. Vậy nếu chương trình của bạn lớn hơn và muốn có nhiều projects (console, class hay tests) trong cùng 1 chỗ và quản lý chung thì làm sao?

 

Với những ai đã sử dụng qua Visual Studio (VS) thì việc này rất đơn giản vì mặc định bạn tạo project trên VS là bạn đã nằm trong 1 solution và có thêm nhiều projects vào trong solution đó. Tuy nhiên, khi sử dụng Visual Studio Code (VSCode) và dùng .NET Core CLI thì việc này không đến một cách mặc định tự nhiên nữa. Các câu lệnh dưới đây sẽ giúp bạn tạo 1 .NET Core solution:

 

cd Desktop # chuyển đường dẫn đến chỗ bạn muốn chứa solution
dotnet new sln -o UnitTestXunit # tạo 1 solution tên UnitTestXunit

 

Quan tâm đến lập trình, bạn đang tìm hiểu về C#, hay muốn bắt đầu một công việc mới hay nâng cao kĩ năng, Techmaster luôn có chương trình thực tập giúp các bạn phát triển kĩ năng cũng như hỗ trợ tìm kiếm công việc. 

Tạo Các Projects Bên Trong Solution

Bây giờ bạn đã 1 .NET Core solution và có thể tạo ra nhiều projects con bên trong. Chúng ta sẽ tạo 1 MathLib class và 1 xUnit test project.

Tạo C# Class

cd UnitTestXunit 
dotnet new classlib -o MathLib # Tạo 1 MathLib class
dotnet sln add ./MathLib/MathLib.csproj # Thêm class project vào trong solution

Tạo xUnit Test Project

Với test project, chúng ta cũng cần thêm reference đến class MathLib mà ta sẽ kiểm thử

mkdir MathLib.Tests
cd MathLib.Tests
dotnet new xunit # Tạo xUnit test template
dotnet add reference ../MathLib/MathLib.csproj # Thêm reference đến class cần kiểm thử
cd ..
dotnet sln add ./MathLib.Tests/MathLib.Tests.csproj

Sau khi hoàn thành các bước trên, bạn sẽ có 1 solution UnitTestXunit trong đó chứa 2 projects con là class MathLib và xUnit test project MathLib.Tests như sau

unit test solution structure

Viết Test Cases và Class 

Giả lập MathLib class của chúng ta sẽ có các hàm để tính toán. Tên class mặc định là Class1, tôi sẽ đổi cả tên file `Class1.cs` và tên class trong file thành *SimpleMath*. Class này chứa 4 phương thức tính cộng, trừ, nhân, chia, số dư của số nguyên như sau

 

// ./MathLib/SimpleMath.cs
using System;

namespace MathLib
{
    public class SimpleMath
    {
        // static public int Add(int a, int b) 
        // {
        //     return a + b;
        // }
        // Thay vì viết đầy đủ như trên, có thể dùng syntax ngắn gọn hơn
        static public int Add(int a, int b) =>  a + b; // expression-bodied function in C# 6.0
        static public int Subtract(int a, int b) => a - b;
        static public int Multiply(int a, int b) => a * b;
        static public int Divide(int a, int b) => a / b;
        static public int Remainder(int a, int b) => a % b;
    }
}

Viết test cases kiểm thử các phương thức trên

Chuyển sang folder MathLib.Tests, mặc định file test ban đầu là `UnitTest1.cs` và tên class cũng vậy, tôi sẽ đổi thành *SimpleMathTest*. Lúc này class SimpleMathTest sẽ là 1 bộ test dùng để kiểm thử class SimpleMath của MathLib. Mỗi phương thức trong class này sẽ là 1 test case. Lưu ý, nên đặt tên phương thức có nghĩa, ví dụ kiểm thử phương thức `Add` thì tên test case cũng như tên phương thức là `AddTest`, không nên chỉ để `Test1`, `Test2`,...

 

using System;
using Xunit;
using MathLib;

namespace MathLib.Tests
{
    public class SimpleMathTest
    {
        [Fact]
        public void AddTest()
        {
            var res = SimpleMath.Add(1, 2);
            Assert.True(3 == res);    
        }
        [Fact]
        public void SubtractTest()
        {
            var res = SimpleMath.Subtract(10, 3);
            Assert.True(7 == res);    
        }
        [Fact]
        public void MultiplyTest()
        {
            var res = SimpleMath.Multiply(7, 6);
            Assert.Equal(42, res);    
        }
        [Fact]
        public void DivideTest()
        {
            var res = SimpleMath.Divide(21, 3);
            Assert.Equal( 7, res);    
        }
        [Fact]
        public void RemainderTest()
        {
            var res = SimpleMath.Remainder(19, 4);
            Assert.Equal( 3, res);    
        }
    }
}

 

Trong 1 test framework, bạn sẽ có các hàm để so sánh kết quả. xUnit có các `Assertion` phương thức, trong ví dụ trên, để so sánh 2 kết quả bạn có thể dùng `Assert.Equal(kết quả mong đợi, kết quả thực tế)` hoặc `Assert.True(phép so sánh)`. 

[Fact] vs [Theory]

Trong xUnit, bạn có 2 thuộc tính (attributes) là `[Fact]` và `[Theory]` để xác định 2 kiểu test cases. Với `[Fact]` bạn có 1 unit test bình thường như mọi framework khác và phương thức test này không nhận tham số đầu vào nào hết.

 

`[Theory]` thường được dùng nếu bạn muốn chạy cùng 1 test với 1 bộ dữ liệu, phương thức test lúc này sẽ nhận tham số đầu vào. Đi cùng với [Theory], bạn có thể dùng thêm `[InlineData()]` để truyền dữ liệu cho test cases. Áp dụng `[Theory]`, ta có thể viết test cases như sau:

 

using System;
using Xunit;
using MathLib;

namespace MathLib.Tests
{
    public class SimpleMathTest
    {
        [Theory]
        [InlineData(1, 2, 3)]
        [InlineData(-10, 2, -8)]
        [InlineData(100, 72, 172)]        
        public void AddTest(int a, int b, int expectedRes)
        {
            var res = SimpleMath.Add(a, b);
            Assert.True(expectedRes == res);    
        }
        [Theory]
        [InlineData(1, 2, -1)]
        [InlineData(-25, -10, -15)]
        [InlineData(9, 5, 4)]
        public void SubtractTest(int a, int b, int expectedRes)
        {
            var res = SimpleMath.Subtract(a, b);
            Assert.True(expectedRes == res);    
        }
        [Theory]
        [InlineData(1, 2, 2)]
        [InlineData(-25, -10, 250)]
        [InlineData(0, 5, 0)]
        public void MultiplyTest(int a, int b, int expectedRes)
        {
            var res = SimpleMath.Multiply(a, b);
            Assert.Equal(expectedRes, res);    
        }
        [Theory]
        [InlineData(100, 2, 50)]
        [InlineData(-25, 1, -25)]
        [InlineData(0, 5, 0)]
        public void DivideTest(int a, int b, int expectedRes)
        {
            var res = SimpleMath.Divide(a, b);
            Assert.Equal( expectedRes, res);    
        }
        [Theory]
        [InlineData(100, 2, 0)]
        [InlineData(-25, 4, -1)]
        [InlineData(11, 3, 2)]
        public void RemainderTest(int a, int b, int expectedRes)
        {
            var res = SimpleMath.Remainder(a, b);
            Assert.Equal( expectedRes, res);    
        }
    }
}

Các bạn có thể thấy, khi chúng ta có nhiều bộ dữ liệu muốn kiểm tra, ta có thể dùng `[Theory]` để chạy 1 test case qua tất cả các trường hợp dữ liệu.

Chạy Test

Để chạy test, bạn có thể gọi `dotnet test` luôn khi đang ở ngoài solution hoặc chỏ đường dẫn trên terminal vào MathLib.Tests

dotnet test

Nếu test thất bại, xUnit sẽ thông báo test case nào thất bại cũng như kết quả của test case đó

fail test cases

Nếu tất cả thành công, sẽ thông báo 

pass test cases

Chúng ta đã viết được unit tests đơn giản với .NET Core và VSCode. Hi vọng là với hướng dẫn ban đầu này, các bạn có thể bắt đầu viết unit tests với .NET Core. Ví dụ trên là cực kì đơn giản, bạn có thể thử nghiệm thêm các test cases và cập nhật các phương thức trong MathLib chặt chẽ hơn, ví dụ nếu gọi `Divide(5, 0)` thì việc gì sẽ xảy ra. Các bạn có thể tìm hiểu thêm về xUnit và các test frameworks khác được hỗ trợ trong .NET Core là NUnitMSTest.

 

Quan tâm đến lập trình, bạn đang tìm hiểu về C#, hay muốn bắt đầu một công việc mới hay nâng cao kĩ năng, Techmaster luôn có chương trình thực tập giúp các bạn phát triển kĩ năng cũng như hỗ trợ tìm kiếm công việc.