Hướng Dẫn Extent Reports: Tạo Báo Cáo Test “Đẹp Lung Linh” Trong Selenium

Lời mở đầu

Chào các bạn,

Bạn đã bao giờ chạy xong 100 test case, có 5 cái Failed, nhưng không biết nó Failed ở đâu và tại sao chưa? Bạn phải lục lọi trong mớ log rối rắm để tìm nguyên nhân?

Extent Reports sẽ giải quyết triệt để vấn đề này. Nó cung cấp:

  • Dashboard trực quan: Biểu đồ Pie chart thể hiện bao nhiêu Pass/Fail.
  • Chi tiết từng bước: Log lại từng hành động (Click, Type, Verify).
  • Screenshot: Tự động chụp ảnh màn hình ngay tại thời điểm lỗi.
  • Giao diện: Đẹp như một trang web hiện đại, có thể gửi file HTML này qua email cho sếp.

Hôm nay, tôi sẽ hướng dẫn các bạn tích hợp Extent Reports phiên bản 5 (mới nhất) vào dự án Selenium + TestNG.


Chuẩn bị thư viện (Dependencies)

Đầu tiên, chúng ta cần thêm thư viện Extent Reports vào pom.xml.

Bước 1: Mở file pom.xml. Bước 2: Thêm dependency sau vào thẻ <dependencies>:

<dependency>
    <groupId>com.aventstack</groupId>
    <artifactId>extentreports</artifactId>
    <version>5.1.1</version>
</dependency>

Lưu ý: Đừng quên chuột phải vào Project > Maven > Update Project để tải thư viện về nhé!


Cấu hình Extent Reports cơ bản

Extent Reports hoạt động dựa trên 3 thành phần chính:

  1. ExtentSparkReporter: Cấu hình giao diện báo cáo (Title, Theme, tên file).
  2. ExtentReports: Bộ máy chính để tạo và tổng hợp thông tin báo cáo.
  3. ExtentTest: Đại diện cho từng Test Case (dùng để log Pass/Fail).

Chúng ta sẽ tạo một class quản lý việc sinh báo cáo. Hãy tạo class ExtentManager trong package com.automation.utils.

package com.automation.utils;

import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.reporter.ExtentSparkReporter;
import com.aventstack.extentreports.reporter.configuration.Theme;

public class ExtentManager {
    private static ExtentReports extent;

    public static ExtentReports createInstance() {
        if (extent == null) {
            // Tạo file báo cáo tên là 'extent-report.html'
            ExtentSparkReporter sparkReporter = new ExtentSparkReporter("extent-report.html");
            
            // Cấu hình giao diện
            sparkReporter.config().setTheme(Theme.DARK); // Giao diện tối cho ngầu
            sparkReporter.config().setDocumentTitle("Automation Test Report");
            sparkReporter.config().setReportName("Kết quả kiểm thử Regression");

            extent = new ExtentReports();
            extent.attachReporter(sparkReporter);
            
            // Thêm thông tin hệ thống (Optional)
            extent.setSystemInfo("Tester", "Hoc Vien");
            extent.setSystemInfo("OS", System.getProperty("os.name"));
            extent.setSystemInfo("Java Version", System.getProperty("java.version"));
        }
        return extent;
    }
}


Tích hợp TestNG Listener (Cách làm PRO)

Người mới thường viết code log report ngay trong hàm @Test. Đừng làm vậy! Code sẽ rất rối. Cách chuyên nghiệp là sử dụng TestNG Listener. Listener sẽ “lắng nghe” sự kiện: khi nào test bắt đầu, khi nào test xong, khi nào test lỗi… và tự động ghi log.

Tạo class TestListener trong package com.automation.utils và implements ITestListener.

package com.automation.utils;

import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.Status;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;

public class TestListener implements ITestListener {
    
    private static ExtentReports extent = ExtentManager.createInstance();
    private static ThreadLocal<ExtentTest> test = new ThreadLocal<>();

    @Override
    public void onStart(ITestContext context) {
        System.out.println("Bắt đầu chạy Suite...");
    }

    @Override
    public void onTestStart(ITestResult result) {
        // Tạo node test mới trong report khi bắt đầu 1 @Test
        ExtentTest extentTest = extent.createTest(result.getMethod().getMethodName());
        test.set(extentTest);
    }

    @Override
    public void onTestSuccess(ITestResult result) {
        test.get().log(Status.PASS, "Test Case Passed");
    }

    @Override
    public void onTestFailure(ITestResult result) {
        test.get().log(Status.FAIL, "Test Case Failed is: " + result.getName());
        test.get().log(Status.FAIL, "Lỗi: " + result.getThrowable());
        
        // TODO: Chụp màn hình (Screenshot) thêm vào đây (Xem phần dưới)
    }

    @Override
    public void onTestSkipped(ITestResult result) {
        test.get().log(Status.SKIP, "Test Case Skipped");
    }

    @Override
    public void onFinish(ITestContext context) {
        // Lưu báo cáo
        if (extent != null) {
            extent.flush();
        }
    }
}


Lab thực hành: Chụp ảnh màn hình khi Failed

Báo cáo mà không có ảnh lỗi thì cũng vô dụng. Chúng ta sẽ thêm chức năng: Nếu Test Fail -> Tự chụp màn hình -> Đính kèm vào Report.

Bước 1: Hàm chụp ảnh Base64

Thêm hàm này vào TestListener hoặc một class Utils riêng. Dùng Base64 giúp ảnh nhúng trực tiếp vào file HTML, không cần lưu file ảnh rời rạc.

// Import thêm Selenium
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;

// Trong TestListener, cần lấy được driver từ Test Class
// Giả sử Test Class của bạn có biến 'public WebDriver driver'
public String captureScreenshot(WebDriver driver) {
    TakesScreenshot ts = (TakesScreenshot) driver;
    return ts.getScreenshotAs(OutputType.BASE64);
}

Bước 2: Cập nhật onTestFailure

Sửa lại hàm onTestFailure trong TestListener.java:

@Override
public void onTestFailure(ITestResult result) {
    test.get().log(Status.FAIL, "Test Case Failed");
    test.get().log(Status.FAIL, result.getThrowable());

    // Lấy driver từ Test Class hiện tại
    try {
        Object currentClass = result.getInstance();
        WebDriver driver = ((BaseTest) currentClass).getDriver(); // Giả sử bạn có BaseTest
        
        if (driver != null) {
            String base64Screenshot = captureScreenshot(driver);
            // Đính kèm ảnh vào report
            test.get().addScreenCaptureFromBase64String(base64Screenshot, "Ảnh màn hình lỗi");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Góc tương tác: Để đoạn code trên hoạt động trơn tru, các Test Class của bạn nên kế thừa từ một class cha là BaseTest có chứa getter cho driver nhé.


Chạy thử nghiệm (Lab)

Bây giờ hãy tạo một Test Class và cố tình làm nó Fail để xem báo cáo.

DemoReportTest.java:

package com.automation.tests;

import com.automation.utils.TestListener;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners; // Import này quan trọng
import org.testng.annotations.Test;

// Kích hoạt Listener
@Listeners(TestListener.class)
public class DemoReportTest {
    
    public WebDriver driver; // Public để Listener truy cập được

    // Getter cho Listener gọi
    public WebDriver getDriver() {
        return driver;
    }

    @BeforeMethod
    public void setup() {
        driver = new ChromeDriver();
        driver.get("https://automationexercise.com/");
    }

    @Test
    public void testPass() {
        Assert.assertTrue(driver.getTitle().contains("Automation"));
    }

    @Test
    public void testFail() {
        // Cố tình Fail để xem screenshot
        Assert.assertEquals(driver.getTitle(), "Sai Title Ne"); 
    }

    @AfterMethod
    public void tearDown() {
        driver.quit();
    }
}

Sau khi chạy xong, hãy Refresh Project trong Eclipse. Bạn sẽ thấy file extent-report.html. Chuột phải chọn Open With > Web Browser.

Bùm! Một báo cáo Dashboard hiện ra với biểu đồ Pie Chart và ảnh chụp màn hình tại case bị Fail.


Mini Project cuối buổi

Hãy nâng cấp dự án của bạn lên một tầm cao mới.

Đề bài: Tạo một bộ Test Suite kiểm tra trang https://www.demoblaze.com/ gồm 3 test case:

  1. TC01 (Pass): Verify Logo trang chủ hiển thị.
  2. TC02 (Fail): Cố tình verify sai giá tiền của một sản phẩm (để kích hoạt chụp ảnh).
  3. TC03 (Skip): Dùng throw new SkipException("Skip message").

Yêu cầu:

  • Sử dụng @Listeners(TestListener.class).
  • Xuất ra file MyProject-Report.html.
  • Gửi file báo cáo đó cho mình xem (bằng cách chụp ảnh màn hình dashboard và comment bên dưới).

Tổng kết và bài tập về nhà

Hôm nay chúng ta đã biến những dòng log khô khan thành một bản báo cáo sinh động, chuyên nghiệp. Đây là vũ khí giúp bạn ghi điểm tuyệt đối trong mắt khách hàng.

Tóm tắt kiến thức:

  • ExtentSparkReporter: Cấu hình giao diện.
  • ITestListener: Tự động bắt sự kiện Pass/Fail.
  • Screenshot: Bằng chứng quan trọng nhất khi fix bug.

Bài tập về nhà: Hãy thử tìm hiểu cách tích hợp file testng.xml để chạy Listener cho toàn bộ dự án mà không cần thêm @Listeners vào từng class riêng lẻ. (Gợi ý: Thẻ <listeners> trong XML).

Chủ đề tiếp theo: Bạn đã có code xịn, report đẹp. Nhưng làm sao để code tự động chạy mỗi đêm hoặc khi Dev đẩy code mới? Bài tới chúng ta sẽ bàn về “CI/CD cơ bản với Jenkins – Tự động hóa quy trình chạy Test”.

Hẹn gặp lại các “Automation Engineer” tương lai!

Leave a comment