0

Viết một test case tốt

Unit testing là một thành phần không thể thiếu của một dự án, vì vậy việc viết test code tốt rất quan trọng trong quá trình phát triển của dự án. Unit testing tốt hỗ trợ và đơn giản hóa quá trình đọc hiểu và bảo trì code của dự án. Nhưng không phải ai cũng biết cách viết test tốt. Trong quá trình tham gia các dự án, cũng như tìm hiều trên mạng, tôi cũng rút ra một số kinh nghiệm viết test cho riêng mình và muốn chia sẻ với mọi người.

Cấu trúc đơn giản để viết một test case tốt đó là "Arrange, Act, Assert" (aka "AAA"). Trong bài viết này, tôi sử dụng tình huống sau: Khi một stack rỗng, chèn một phần tử vào stack thì số lưọng phần tử sẽ tăng lên 1.

public void pushing_an_item_onto_an_empty_stack_increments_count()
{
    // Arrange
    var stack = new Stack<bool>();

    // Act
    stack.Push(false);

    // Assert
    Assert.That(stack.Count, Is.EqualTo(1));
}

Mỗi "giai đoạn" trong 1 test case sẽ theo đúng thứ tự như trên và chỉ xảy ra 1 lần duy nhất. Arrange xuất hiện trước Act, Act đi trước Assert.

Dưới đây là mô tả chi tiến của mỗi giai đoạn

Arrange

Đây là giai đoạn khởi tạo dữ liệu phục vụ cho việc test. Để test được thì chắc chắn cần phải có dữ liệu và cấu hình cần thiết rồi.

Trong ví dụ về stack, arrange chỉ đơn giản là tạo một stack rỗng. Trong các trưòng hợp phức tạp hơn, bạn sẽ cần phải làm nhiều việc hơn để thiết lập dữ liệu.

Act

Đây là giai đoạn test case gọi hàm cần test. Trong ví dụ về stack, "Act" xảy ra khi Push() được gọi.

Assert

Đây là giai đoạn đảm bảo rằng kết quả đầu ra khớp với kỳ vọng của mình. Bạn có thể kết hợp nhiều đối tượng với nhau, gọi nhiều phương thức, nhưng bạn phải gọi câu lệnh assert với những kết quả ý nghĩa. Nếu không có những câu lệnh assert tốt, test case của bạn chỉ đảm bảo rằng đoạn code mà bạn đang check không bị crash, vì vậy đấy không được xem là 1 test case tốt.

Trong ví dụ stack, giai đoạn "Assert" xảy ra khi ta muốn check thuộc tính Count có khớp với kỳ vọng của mình hay không.

Tại sao sử dụng cấu trúc này

Đây là một cấu trúc nghiêm ngặt, chặt chẽ mà bạn luôn phải bám sát nếu muốn có một test case tốt. Khi test của bạn trở nên phức tạp, bạn thường có xu hướng bẻ gãy cấu trúc này.

Ví dụ, một khi đã vào pha Assert, bạn nên chỉ kiểm tra kết quả, không thực hiện bất kỳ hành động, phương thức nào trên đối tượng mà bạn đang test. Nếu điều này xảy ra, bạn đã phá vỡ cấu trúc AAA.

Dưới đây là ví dụ về một đoạn test tồi:

public void BadPushPopTest()
{
    var stack = new Stack<int>()
    Assert.That(stack.Count, Is.EqualTo(0));
    
    stack.Push(1);
    Assert.That(stack.Count, Is.EqualTo(1));
    
    int popped = stack.Pop();
    Assert.That(popped, Is.EqualTo(1));
    Assert.That(stack.Count, Is.EqualTo(0));
}

Hãy thử chia đoạn code trên thành các pha Arrange, Act, Assert:

public void BadPushPopTest()
{ 
        // Arrange
        var stack = new Stack<int>();
        Assert.That(stack.Count, Is.EqualTo(0));  // Warning: asserting in the Arrange phase!

        // Act
        stack.Push(1);

        // Assert
        Assert.That(stack.Count, Is.EqualTo(1)); 

        int popped = stack.Pop();                      // Warning: action in the Assert phase!
        Assert.That(popped, Is.EqualTo(1));       // Warning: why are we asserting again?
        Assert.That(stack.Count, Is.EqualTo(0)); // Warning: and again…
}

Như bạn thấy ta không thể chia đoạn test trên theo cấu trúc AAA. Dẫn đến đoạn test trên rất khó để đọc và đưa ra một mô tả chính xác cho nó.

Tham khảo

http://defragdev.com/blog/?p=783


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí