Cơ Chế Tự Động Lặp Lại Bài Kiểm Thử Thất Bại trong TestNG

Triển khai cơ chế tự động chạy lại bài kiểm thử thất bại giúp nâng cao độ tin cậy của bộ kiểm thử. Dưới đây là hướng dẫn chi tiết với các bước tối ưu hóa:

1. Xây dựng bộ phân tích lặp lại

Tạo lớp RetryMechanism triển khai giao diện IRetryAnalyzer để quản lý số lần thử lại và điều kiện kích hoạt:

import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;

public class RetryMechanism implements IRetryAnalyzer {
    private int retryLimit = 1;
    private int attemptCount = 0;

    @Override
    public boolean retry(ITestResult result) {
        if (attemptCount < retryLimit) {
            System.out.println("Đang thử lại bài kiểm thử " + result.getMethod().getMethodName() 
                + " (lần " + (attemptCount + 1) + ") - Trạng thái: " + getStatusLabel(result.getStatus()));
            incrementAttempt();
            return true;
        }
        resetCounter();
        return false;
    }

    private String getStatusLabel(int status) {
        return switch (status) {
            case ITestResult.SUCCESS -> "THÀNH CÔNG";
            case ITestResult.FAILURE -> "THẤT BẠI";
            case ITestResult.SKIP -> "BỎ QUA";
            default -> "KHÔNG XÁC ĐỊNH";
        };
    }

    private void incrementAttempt() {
        attemptCount++;
    }

    private void resetCounter() {
        attemptCount = 0;
    }
}

2. Thiết lập bộ lắng nghe chú thích

Lớp RetryConfigurator triển khai IAnnotationTransformer để tự động áp dụng cơ chế lặp lại:

import org.testng.IAnnotationTransformer;
import org.testng.annotations.ITestAnnotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class RetryConfigurator implements IAnnotationTransformer {
    @Override
    public void transform(
        ITestAnnotation annotation, 
        Class<?> testClass,
        Constructor<?> testConstructor,
        Method testMethod
    ) {
        if (annotation.getRetryAnalyzer() == null) {
            annotation.setRetryAnalyzer(RetryMechanism.class);
        }
    }
}

3. Xử lý kết quả kiểm thử

Lớp ResultCleaner loại bỏ các bản ghi trùng lặp trong báo cáo khi sử dụng DataProvider:

import org.testng.*;
import java.util.*;

public class ResultCleaner implements ITestListener {
    private Set<Integer> uniqueIds = new HashSet<>();

    @Override
    public void onFinish(ITestContext context) {
        cleanFailedTests(context.getFailedTests());
    }

    private void cleanFailedTests(IResultMap results) {
        List<ITestResult> duplicates = new ArrayList<>();
        
        for (ITestResult result : results.getAllResults()) {
            int key = generateUniqueKey(result);
            
            if (uniqueIds.contains(key)) {
                duplicates.add(result);
            } else {
                uniqueIds.add(key);
            }
        }
        
        for (ITestResult duplicate : duplicates) {
            results.removeResult(duplicate);
        }
    }

    private int generateUniqueKey(ITestResult result) {
        return Objects.hash(
            result.getTestClass().getName(),
            result.getMethod().getMethodName(),
            Arrays.hashCode(result.getParameters())
        );
    }
}

4. Cấu hình trong testng.xml

<?xml version="1.0" encoding="UTF-8"?>
<suite name="Kiểm thử tự động">
    <listeners>
        <listener class-name="vn.qc.retry.RetryConfigurator"/>
        <listener class-name="vn.qc.retry.ResultCleaner"/>
    </listeners>
    <test name="Bộ kiểm thử chính">
        <classes>
            <class name="vn.qc.retry.SampleTest"/>
        </classes>
    </test>
</suite>

5. Triển khai bài kiểm thử mẫu

package vn.qc.retry;

import org.testng.annotations.*;

public class SampleTest {
    @Test(dataProvider = "testData")
    public void validationTest(int id, String value) {
        System.out.println("Kiểm tra với giá trị: " + value);
        assert false : "Kịch bản kiểm thử chủ động thất bại";
    }

    @DataProvider
    public Object[][] testData() {
        return new Object[][] {
            {1, "Dữ liệu A"},
            {2, "Dữ liệu B"},
            {3, "Dữ liệu C"}
        };
    }
}

Kết quả thực thi với TestNG phiên bản 6.9.13.6 trở lên sẽ hiển thị mỗi bài kiểm thử thất bại được thử lại đúng 1 lần:

Kiểm tra với giá trị: Dữ liệu A
Đang thử lại bài kiểm thử validationTest (lần 1) - Trạng thái: THẤT BẠI
Kiểm tra với giá trị: Dữ liệu A
Kiểm tra với giá trị: Dữ liệu B
Đang thử lại bài kiểm thử validationTest (lần 1) - Trạng thái: THẤT BẠI
Kiểm tra với giá trị: Dữ liệu B
Kiểm tra với giá trị: Dữ liệu C
Đang thử lại bài kiểm thử validationTest (lần 1) - Trạng thái: THẤT BẠI
Kiểm tra với giá trị: Dữ liệu C

Lưu ý: Phiên bản TestNG trước 6.9.13.6 có lỗi nhân đôi số lần thử với DataProvider, cần cập nhật theo hướng dẫn tại GitHub PR #740 và #1104.

Thẻ: testng Retry-Mechanism test-automation

Đăng vào ngày 28 tháng 6 lúc 17:58