NỘI DUNG BÀI HỌC

✅ Listener trong TestNG là gì?
✅ Các loại Listeners trong TestNG
✅ ITestListener trong TestNG
✅ Cách riển khai ITestListener trong TestNG

✅ TestNG Listeners là gì?

Điều đầu tiên xuất hiện trong đầu khi đọc thuật ngữ "Listeners" là hiểu như nó đang lắng nghe một thứ gì đó 😁

TestNG Listeners là đoạn mã được thiết kế sẵn để đọc hiểu các sự kiện xảy ra trong TestNG. Nếu sự kiện khớp với sự kiện mà chúng ta muốn Listeners hiểu thì nó sẽ thực thi mã code đó cho chúng ta.

Điều này sẽ dẫn đến việc sửa đổi hành vi mặc định của TestNG.

Ví dụ, chúng ta chỉ muốn in lỗi ngoại lệ lên các báo cáo nếu thử nghiệm không thành công. Ở đây, chúng ta có thể áp dụng một TestNG Listeners sẽ theo dõi và hiểu sự kiện "fail of test case" và khi nó xảy ra, nó sẽ ghi lại lỗi đó. Mình sẽ custom tùy ý với các loại trong Listeners của nó hỗ trợ.

Listeners TestNG được áp dụng làm giao diện trong code vì Listeners cũng chỉ là một interface trong TestNG. Nó cung cấp cho chúng ta các hàm xử lý trong giao diện class Listener đó để lắng nghe được nhịp điệu trạng thái khi mà mình bắt đầu chạy test.

Nó được sử dụng trong Selenium bằng cách triển khai giao diện Listeners (Interface). Nó cho phép tùy chỉnh các báo cáo hoặc nhật ký trong TestNG. Có nhiều loại trình nghe TestNG có sẵn chúng ta sẽ thảo luận về những điều này trong phần tiếp theo.


✅ Các loại Listeners trong TestNG

  1. ITestListener
  2. IReporter
  3. ISuiteListener
  4. IInvokedMethod
  5. IHookable
  6. IConfigurationListener
  7. IConfigurableListener
  8. IAnnotationTransformer
  9. IExecution
  10. IMethodInterceptor


Chúng ta sẽ học:

  1. ITestListener



✅ ITestListener trong TestNG

ITestListener có các phương pháp sau:

onStart: Phương thức này gọi khi lớp thử nghiệm được khởi tạo và trước khi thực thi bất kỳ phương thức thử nghiệm nào.
Cú pháp: void onStart (ITestContext context){...}

onFinish: Phương thức này gọi khi tất cả các phương thức thử nghiệm đã chạy và việc gọi tất cả các phương thức cấu hình của chúng sẽ xảy ra.
Cú pháp: void onFinish (ITestContext context){...}

onTestStart: Phương thức này gọi mỗi khi một phương thức thử nghiệm được gọi và thực thi.
Cú pháp: void onTestStart (ITestResult result){...}

onTestSuccess: Phương thức này được gọi mỗi khi một ca kiểm thử vượt qua (thành công).
Cú pháp: void onTestSuccess (ITestResult result){...}

onTestFailure: Phương thức này gọi mỗi khi một trường hợp thử nghiệm không thành công.
Cú pháp: void onTestFailure (ITestResult result){...}

onTestSkipped: Phương thức này gọi mỗi khi thử nghiệm bỏ qua.
Cú pháp: void onTestSkipped (ITestResult result){...}

onTestFailedButWithinSuccessPercentage: Phương thức này gọi khi phương pháp thử nghiệm không thành công toàn bộ nhưng đã vượt qua một phần trăm thành công nhất định, được xác định bởi người dùng.
Cú pháp: void onTestFailedButSuccessPercentage (ITestResult result){...}


✅ Làm thế nào để triển khai ITestListener trong TestNG?

Phần này sẽ giải thích từng bước cách sử dụng trình lắng nghe trong TestNG để gọi các chức năng khác nhau. Điều quan trọng cần lưu ý ở đây là Người nghe có thể triển khai theo hai cách trong TestNG:

  • Ở cấp độ Class: Sử dụng annotation @Listeners  trên mỗi class testcase.
  • Ở cấp độ Suite: Sử dụng tag <Listeners> trong tệp XML TestNG.


🔆 Triển khai ITestListener ở cấp độ Class

Để triển khai ITestListener, chúng ta cần tạo một lớp với tất cả các phương thức mà chúng ta muốn ghi đè. Ví dụ đặt nó là TestListener.java

Nhớ là ghi đè thì phải có @Override vì mình đang gọi lại từ interface ITestListener

import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;

public class TestListener implements ITestListener {

    @Override
    public void onStart(ITestContext arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onFinish(ITestContext arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onTestStart(ITestResult arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onTestSuccess(ITestResult arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onTestFailure(ITestResult arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onTestSkipped(ITestResult arg0) {
        // TODO Auto-generated method stub

    }
}


Và bây giờ chúng ta muốn custom thằng nào thì sửa trong nội dung hàm của thằng đó thôi.

Ví dụ sửa các hàm bên dưới với dòng lệnh in ra tên testcase chẵn hạn:

import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;

public class TestListener implements ITestListener {

    @Override
    public void onFinish(ITestContext result) {

    }

    @Override
    public void onStart(ITestContext result) {

    }

    @Override
    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {

    }

    @Override
    public void onTestFailure(ITestResult result) {
        System.out.println("Đây là test case bị fail: " + result.getName());

    }

    @Override
    public void onTestSkipped(ITestResult result) {
        System.out.println("Đây là test case bị bỏ qua: " + result.getName());

    }

    @Override
    public void onTestStart(ITestResult result) {

    }

    @Override
    public void onTestSuccess(ITestResult result) {
        System.out.println("Đây là test case chạy thành công: " + result.getName());

    }
}

 

🔆 Gọi lại TestListener sử dụng


Tiếp theo tạo class Test case để gọi lại Listener. Tạo class tên DemoListener chẳng hạn.

package com.anhtester.testcases;

import anhtester.com.common.TestListener;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.testng.annotations.Listeners;
import org.testng.SkipException;

@Listeners(TestListener.class)
public class DemoListener {

    WebDriver driver;

    @BeforeClass
    public void setupDriver() {
        driver = new ChromeDriver();
    }

    @Test(priority = 1) //Success Test
    public void gotoPage() {
        driver.get("https://anhtester.com");
    }

    @Test(priority = 2) //Failed Test
    public void checkTitle() {
        String expectedTitle = "Anh Tester";
        String originalTitle = driver.getTitle();
        Assert.assertEquals(originalTitle, expectedTitle, "Title of the website do not match");
    }

    @Test(priority = 3)  //Skip Test
    public void skipTest() {
        throw new SkipException("Skipping The Test Method ");
    }

    @AfterClass
    public void closeDriver() {
        driver.quit();
    }
}


Các bạn chú ý chổ @Listeners(TestListener.class) 

@Listeners(tên class Listener mà mình đã setup ở trên hoặc class Listener nào đó khác)

Nhớ chấm class ở đuôi. Syntax: @Listeners(PackageName.ClassName.class) nếu trỏ đến package rồi thì không cần để package vào cũng được.

Mình để nó ở trên đầu của class Test case

Và bây giờ chạy thử class test case

KẾT QUẢ:

ChromeDriver was started successfully.
thg 8 28, 2021 5:08:02 CH org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: W3C
Đây là test case chạy thành công: gotoPage
Đây là test case bị fail: checkTitle

java.lang.AssertionError: Title of the website do not match 
Expected :Anh Tester
Actual   :Anh Tester Automation Testing
<Click to see difference>


	at org.testng.Assert.fail(Assert.java:99)
	at org.testng.Assert.failNotEquals(Assert.java:1037)
	at org.testng.Assert.assertEqualsImpl(Assert.java:140)
	at org.testng.Assert.assertEquals(Assert.java:122)
	at org.testng.Assert.assertEquals(Assert.java:629)
	at RunCode.ListenerTC.checkTitle(ListenerTC.java:35)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:133)
	at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:598)
	at org.testng.internal.TestInvoker.invokeTestMethod(TestInvoker.java:173)
	at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:46)
	at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:824)
	at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:146)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.testng.TestRunner.privateRun(TestRunner.java:794)
	at org.testng.TestRunner.run(TestRunner.java:596)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:377)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:371)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:332)
	at org.testng.SuiteRunner.run(SuiteRunner.java:276)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:96)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1212)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1134)
	at org.testng.TestNG.runSuites(TestNG.java:1063)
	at org.testng.TestNG.run(TestNG.java:1031)
	at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
	at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:109)

Đây là test case bị bỏ qua: skipTest

Test ignored.

===============================================
Default Suite
Total tests run: 3, Passes: 1, Failures: 1, Skips: 1
===============================================


Process finished with exit code 0


Nó đã thực thi các câu thông báo như mình đã thiết lập sẵn. Và mình thấy nó mapping với nhau hết đúng không.

Thực tế hơn chút thì mình có thể gọi cái hàm Capture Screenshot học bài trước vào chổ Pass hoặc Fail để chụp màn hình khi test case fail chẳng hạn.

try {
    CaptureHelper.captureScreenshot(driver, result.getName());
} catch (Exception e) {
    System.out.println("Exception while taking screenshot " + e.getMessage());
}


Thường thì để nó chổ Fail hợp lý hơn, vì lỗi mới chụp màn hình lại làm bằng chứng chứ nó passed rồi thì thôi chụp chi tùm lum cũng không cần thiết.

@Override
public void onTestFailure(ITestResult result) {
    System.out.println("Đây là test case bị fail: " + result.getName());

    try {
        CaptureHelpers.captureScreenshot(driver, result.getName());
    } catch (Exception e) {
        System.out.println("Exception while taking screenshot " + e.getMessage());
    }

}


Ví dụ khác về phương thức onTestFailedButWithinSuccessPercentage trong ITestListener:

Như đã nói bên trên thì thằng này nó sẽ cho biết được thông tin @Test những lần failed nhưng trong đó có phần trăm đã passed thông qua số lần chạy của @Test đó.

Hiểu nôm na chạy test case Login 5 lần mà có 2 lần fail và 3 lần pass thì nó sẽ lấy thông tin của những lần fail đó.

Sử dụng thuộc tính "successPercentage" trong @Test, bạn có thể chỉ định phần trăm thành công được mong đợi từ phương pháp này. Mặc định là (100) tương ứng 100%. Và thuộc tính "invocationCount" để thiết lập số lần chạy của test đó.

@Listeners(MyTestListener.class)
public class SuccessPercentageDemo {

    int count = 0;

    @Test(invocationCount = 5, successPercentage = 50)
    public void kiemTraChanLe() {
        count++;
        System.out.println("Số lần chạy: " + count);

        if (count % 2 == 0) {
            Assert.assertTrue(false);
        } else {
            Assert.assertTrue(true);
        }
    }
}


Nhưng ví dụ trên thì An sẽ tạo biến đếm toàn cục để biết @Test nó chạy bao nhiêu lần.

Truyền vào hàm có 2 thuộc tính thiết lặp như ý nghĩa trên. Nó sẽ chạy 5 lần và trong tỷ lệ phần trăm thành công lớn hơn hoặc bằng 50%.

À còn cái phép tính thì đại khái là nó lấy số lần chạy count đó đem chia cho 2 lấy phần dư để so sánh bằng với 0. (giống kiểm tra số chẵn lẻ ý mà). Trong đó tính mắt thì thấy là có 2 lần fail (count = 2 và count = 4).

Ừ mà thôi kệ bà nó đừng quan tâm nó làm cái gì. Quan tâm cái kết quả nó chạy có lần nào fail và pass thôi là được rồi vì sau này chạy test case đâu có thường tính ba qỹ này đâu chứ hả. Mình chạy với Selenium driver các kiểu cơ mà =))

Và cái tiếp theo quan trọng là nhớ ghi cái gì đó vào chổ method onTestFailedButWithinSuccessPercentage trong ITestListener để dễ nhận biết dấu hiệu là nó có xác nhận được trạng thái xảy ra bên trên nhé =))

public class MyTestListener implements ITestListener {
    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
        System.out.println("Tên của testcase failed nhưng có phần trăm passed là:" + result.getName());
    }
}


Kết quả:

ChromeDriver was started successfully.
thg 12 17, 2021 3:16:05 SA org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: W3C
Số lần chạy: 1
Số lần chạy: 2
Tên của testcase failed nhưng có phần trăm passed là:kiemTraChanLe
java.lang.AssertionError: expected [true] but found [false]
	at org.testng.Assert.fail(Assert.java:99)
	at org.testng.Assert.failNotEquals(Assert.java:1037)
	at org.testng.Assert.assertTrue(Assert.java:45)
	at org.testng.Assert.assertTrue(Assert.java:55)
	at anhtester.com.testcases.ListenerTC.kiemTraChanLe(ListenerTC.java:50)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:133)
	at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:598)
	at org.testng.internal.TestInvoker.invokeTestMethod(TestInvoker.java:173)
	at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:46)
	at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:824)
	at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:146)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.testng.TestRunner.privateRun(TestRunner.java:794)
	at org.testng.TestRunner.run(TestRunner.java:596)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:377)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:371)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:332)
	at org.testng.SuiteRunner.run(SuiteRunner.java:276)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:96)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1212)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1134)
	at org.testng.TestNG.runSuites(TestNG.java:1063)
	at org.testng.TestNG.run(TestNG.java:1031)
	at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
	at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:109)
Số lần chạy: 3
Số lần chạy: 4
java.lang.AssertionError: expected [true] but found [false]
Tên của testcase failed nhưng có phần trăm passed là:kiemTraChanLe
	at org.testng.Assert.fail(Assert.java:99)
	at org.testng.Assert.failNotEquals(Assert.java:1037)
	at org.testng.Assert.assertTrue(Assert.java:45)
	at org.testng.Assert.assertTrue(Assert.java:55)
	at anhtester.com.testcases.ListenerTC.kiemTraChanLe(ListenerTC.java:50)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:133)
	at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:598)
	at org.testng.internal.TestInvoker.invokeTestMethod(TestInvoker.java:173)
	at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:46)
	at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:824)
	at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:146)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.testng.TestRunner.privateRun(TestRunner.java:794)
	at org.testng.TestRunner.run(TestRunner.java:596)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:377)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:371)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:332)
	at org.testng.SuiteRunner.run(SuiteRunner.java:276)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:96)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1212)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1134)
	at org.testng.TestNG.runSuites(TestNG.java:1063)
	at org.testng.TestNG.run(TestNG.java:1031)
	at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
	at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:109)
Số lần chạy: 5

===============================================
Default Suite
Total tests run: 5, Passes: 3, Failures: 2, Skips: 0
===============================================


Nó báo sai ở lần chạy 2 và 4 như mình cố tình thiết lập trước đó.

 

🔆 Triển khai ITestListener ở cấp độ Suite

Để triển khai ITestListener ở cấp Suite, thì chúng ta xóa chú thích @Listeners khỏi class Test case đi và thiết lập nó trong tệp XML.

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >

<suite name="Suite">

    <listeners>
        <listener class-name="com.anhtester.listeners.TestListener"></listener>
    </listeners>

    <test name="Test">
        <classes>
            <class name="com.anhtester.testcases.DemoListener">
            </class>
        </classes>
    </test>
</suite>


Đấy. Nó chỉ vậy thôi.


🔆 Kiểm tra trạng thái Test Case sau khi chạy tại BaseTest

Các bạn có cách khác để kiểm tra và bắt được trạng thái của Test case hoặc tên Test case đó sau khi chạy. Nhưng cách này không cần dùng khi mà mình đã dùng TestListener bên trên.

Dùng class ITestResult và kết hợp ghi chú @AfterMethod để bắt trạng thái sau khi chạy từng test case (@Test)

@AfterMethod
    public void checkAfterMethod(ITestResult result) {
        //Bắt trạng thái hoặc tên Test case sau khi chạy xong để xử lý gì đó
        if (ITestResult.FAILURE == result.getStatus()) {
            System.out.println(result.getName() + " failed");
        }
        else if(ITestResult.SUCCESS == result.getStatus()){
            System.out.println(result.getName() + " passed");
        }
        else{
            System.out.println(result.getName() + " skipped");
        }
    }


ITestResult
chấm status (FAILURE, SUCCESS, SKIP,...) là lấy trạng thái chuẩn của thư viện ITestResult để so sánh với trạng thái  result.getStatus() của từng test case mà mình viết ra sau khi chạy xong để so sánh.

Giá trị của result.getStatus() là con số 1, 2, 3 tương ứng SUCCESS, FAILURE, SKIP

[Selenium Java] Bài: TestNG Listeners | Anh Tester

Chúng ta không nên dùng bên này vì bên TestListener đã có đầy đủ hết rồi. Cái này An chỉ cho biết thôi nhé.

Teacher

Teacher

Anh Tester

Software Quality Engineer

Đường dẫu khó chân vẫn cần bước đi
Đời dẫu khổ tâm vẫn cần nghĩ thấu

Cộng đồng Automation Testing Việt Nam:

🌱 Telegram Automation Testing:   Cộng đồng Automation Testing
🌱 
Facebook Group Automation: Cộng đồng Automation Testing Việt Nam
🌱 
Facebook Fanpage: Cộng đồng Automation Testing Việt Nam - Selenium
🌱 Telegram
Manual Testing:   Cộng đồng Manual Testing
🌱 
Facebook Group Manual: Cộng đồng Manual Testing Việt Nam

Chia sẻ khóa học lên trang

Bạn có thể đăng khóa học của chính bạn lên trang Anh Tester để kiếm tiền

Danh sách bài học