Kiểm thử đơn vị tự động là một kỹ thuật kiểm thử phần mềm trong đó các đơn vị hoặc thành phần riêng lẻ của ứng dụng phần mềm được kiểm tra tách biệt với phần còn lại của ứng dụng. Mục tiêu của kiểm thử đơn vị tự động là để xác nhận rằng mỗi đơn vị hoặc thành phần của ứng dụng đang hoạt động chính xác và đáp ứng các yêu cầu đã chỉ định.

Trích giáo trình lớp luyện thi chứng chỉ CNTT Nhật Bản https://fe.techmaster.vn

Các bài kiểm thử đơn vị thường được tạo bởi các nhà phát triển phần mềm và được chạy tự động như một phần của quy trình xây dựng. Điều này cho phép các nhà phát triển nhanh chóng xác định và khắc phục mọi sự cố được phát hiện trong quá trình thử nghiệm. Kiểm thử đơn vị tự động thường kết hợp trong phương pháp Test Driven Developent.

Kiểm thử đơn vị tự động có một số lợi ích, bao gồm:

  • Chất lượng được cải thiện: Các bài kiểm thử đơn vị tự động có thể giúp phát hiện sớm các lỗi trong quá trình phát triển, điều này có thể tiết kiệm thời gian và công sức về lâu dài.

  • Phản hồi nhanh hơn: Có thể chạy thử nghiệm đơn vị tự động nhanh chóng, cung cấp phản hồi nhanh cho nhà phát triển về chất lượng mã của họ.

  • Bảo trì dễ dàng hơn: Các bài kiểm thử đơn vị tự động có thể giúp đảm bảo rằng các thay đổi đối với mã không phá vỡ chức năng hiện có, giúp việc bảo trì và cải tiến phần mềm theo thời gian trở nên dễ dàng hơn.

Automated Test

Các thư viện kiểm thử tự động phổ biến

  • JUnit: kiểm thử cho Java, hiện phiên bản mới nhất là JUnit5
  • NUnit: cho ngôn ngữ .NET, chẳng hạn như C# và VB.NET. Nó tương tự như JUnit và có nhiều tính năng giống nhau.
  • xUnit: xUnit là một nhóm các khung kiểm thử đơn vị bao gồm các khung cho các ngôn ngữ lập trình khác nhau, bao gồm C#, F# và VB.NET.
  • PyTest: cho Python, có rất nhiều plugin
  • PHPUnit: cho PHP
  • RSpec: cho Ruby

Test Driven Development (TDD) vs (Behavior Driven Development) BDD

Có hai cách tiếp cận chính để viết bài kiểm thử đơn vị: phát triển dựa trên kiểm tra (TDD) và phát triển dựa trên hành vi (BDD).

Trong quá trình phát triển dựa trên thử nghiệm (TDD), nhà phát triển viết một bài kiểm thử đơn vị trước khi viết mã đang được kiểm tra. Điều này đảm bảo rằng mã được viết để đáp ứng các yêu cầu của thử nghiệm. TDD tuân theo quy trình viết bài kiểm tra, chạy bài kiểm tra và thấy nó không thành công, sau đó viết mã để vượt qua bài kiểm tra. Quá trình này giúp nhà phát triển tập trung vào việc viết mã đáp ứng các yêu cầu và đảm bảo rằng mã được kiểm tra kỹ lưỡng.

Trong quá trình phát triển dựa trên hành vi (BDD), nhà phát triển viết một bài kiểm tra mô tả hành vi mong muốn của mã đang được kiểm tra. Bài kiểm tra được viết bằng tiếng Anh đơn giản và dành cho những người không liên quan đến kỹ thuật có thể đọc được. BDD tập trung vào hành vi mong muốn của mã hơn là các chi tiết triển khai.

Cả TDD và BDD đều có thể là cách tiếp cận hiệu quả để viết bài kiểm thử đơn vị và cách nào là tốt nhất phụ thuộc vào nhu cầu và ràng buộc cụ thể của dự án. Một số nhà phát triển thích TDD hơn vì nó giúp họ tập trung vào việc viết mã đáp ứng các yêu cầu và đảm bảo rằng mã được kiểm tra kỹ lưỡng. Những người khác thích BDD hơn vì nó cho phép họ mô tả hành vi mong muốn của mã bằng tiếng Anh đơn giản và làm cho các bài kiểm tra dễ đọc hơn đối với các bên liên quan phi kỹ thuật.

Khi nào không nên viết Automation Unit Test?

Dưới đây là một số tình huống mà kiểm thử đơn vị tự động có thể không phù hợp:

  • Khi mã dự kiến sẽ thay đổi thường xuyên: Nếu mã đang được kiểm tra dự kiến sẽ thay đổi thường xuyên, việc duy trì một bộ lớn các bài kiểm thử đơn vị có thể tốn thời gian và có thể không thực tế.

  • Khi mã khó kiểm tra: Nếu mã đang được kiểm tra có các phụ thuộc phức tạp hoặc khó tách biệt, có thể khó viết các bài kiểm thử đơn vị để kiểm tra chính xác hành vi của nó.

  • Khi chi phí viết và duy trì các bài kiểm thử đơn vị lớn hơn lợi ích mang lại: Trong một số trường hợp, thời gian và nỗ lực cần thiết để viết và duy trì một bộ lớn các bài kiểm thử đơn vị có thể không được chứng minh bằng các lợi ích.

  • Khi mã không quan trọng: Nếu mã đang được kiểm tra không quan trọng và lỗi của nó sẽ không gây hậu quả đáng kể, thì chi phí duy trì kiểm thử đơn vị có thể không hợp lý.


Để các bạn dễ hình dung về Automation Unit Test phần dưới tôi lấy ví dụ kiểm thử hàm bậc hai và phương trình bậc 2 kinh điển

Ví dụ PyTest kiểm thử hàm bậc 2 viết bằng Python

import pytest

def test_quadratic_function():
    from math import isclose

    def quadratic(a, b, c, x):
        return a*x**2 + b*x + c

    # Test case 1: check result for x = 0
    result = quadratic(1, 2, 3, 0)
    assert result == 3, f"Expected 3 but got {result}"

    # Test case 2: check result for x = 1
    result = quadratic(1, 2, 3, 1)
    assert result == 6, f"Expected 6 but got {result}"

    # Test case 3: check result for x = -1
    result = quadratic(1, 2, 3, -1)
    assert result == 0, f"Expected 0 but got {result}"

    # Test case 4: check result for large values of x
    result = quadratic(1, 2, 3, 1e6)
    expected_result = 1e12 + 2e6 + 3
    assert isclose(result, expected_result, rel_tol=1e-9), f"Expected {expected_result} but got {result}"

Ví dụ JUnit kiểm thử giải phương trình bậc 2 viết bằng Java

import org.junit.jupiter.api.*;

import static org.junit.jupiter.api.Assertions.*;

class QuadraticFunctionTest {
  @Test
  void testSolve() {
    QuadraticFunction function = new QuadraticFunction(1, -3, 2);
    double[] roots = function.solve();
    assertEquals(2, roots.length);
    assertEquals(1.0, roots[0], 1e-6);
    assertEquals(2.0, roots[1], 1e-6);
  }
}

Nó tạo ra một đối tượng Hàm bậc hai mới QuadraticFunction với các hệ số 1, -3 và 2, sau đó gọi phương thức giải để tính nghiệm của phương trình bậc hai.

Sau đó, nó sử dụng phương thức assertEquals để xác minh rằng phương thức solve trả về một mảng có hai phần tử và các phần tử của mảng đó bằng với các gốc dự kiến (1.0 và 2.0). Tham số 1e-6 chỉ định chênh lệch tối đa được phép giữa giá trị dự kiến và giá trị thực tế.

Giải thích các annotation quan trọng trong JUnit5

@Test

Chú thích này được sử dụng để đánh dấu một phương thức là một phương thức kiểm tra. Các phương pháp kiểm tra là các phương pháp chứa mã xác minh tính đúng đắn của một số khía cạnh của hệ thống đang được kiểm tra.

@BeforeEach

Chú thích này được sử dụng để đánh dấu một phương thức là phương thức “trước mỗi”. Trước mỗi phương thức là các phương thức được thực thi trước mỗi phương thức kiểm tra trong lớp kiểm tra. Chúng có thể được sử dụng để thiết lập môi trường thử nghiệm hoặc để thực hiện bất kỳ tác vụ nào khác cần thực hiện trước khi mỗi phương pháp thử nghiệm được chạy.

@AfterEach

Chú thích này được sử dụng để đánh dấu một phương thức là một phương thức “sau mỗi”. Sau mỗi phương thức là các phương thức được thực thi sau mỗi phương thức kiểm tra trong lớp kiểm tra. Chúng có thể được sử dụng để làm sạch môi trường thử nghiệm hoặc để thực hiện bất kỳ tác vụ nào khác cần được thực hiện sau mỗi phương pháp thử nghiệm được chạy.

@BeforeAll

Chú thích này được sử dụng để đánh dấu một phương thức là phương thức “trước tất cả”. Trước tất cả các phương thức là các phương thức được thực thi trước bất kỳ phương thức kiểm tra nào trong lớp kiểm tra. Chúng có thể được sử dụng để thiết lập bất kỳ tài nguyên hoặc trạng thái nào cần thiết cho tất cả các phương pháp thử nghiệm.

@AfterAll

Chú thích này được sử dụng để đánh dấu một phương thức là phương thức “sau tất cả”. Sau tất cả các phương thức là các phương thức được thực thi sau tất cả các phương thức kiểm tra trong lớp kiểm tra. Chúng có thể được sử dụng để dọn sạch bất kỳ tài nguyên hoặc trạng thái nào được tạo bởi phương thức before all.

@Disabled

Chú thích này được sử dụng để vô hiệu hóa phương thức kiểm tra hoặc lớp kiểm tra. Các phương thức hoặc lớp thử nghiệm bị vô hiệu hóa không được thực thi khi chạy thử nghiệm.

@Tag

Chú thích này được sử dụng để gán thẻ cho phương thức kiểm tra hoặc lớp kiểm tra. Các thẻ có thể được sử dụng để nhóm các bài kiểm tra hoặc để chỉ định các đặc điểm nhất định của các bài kiểm tra.