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 các thư viện Test Automation với ngôn ngữ lập trình Java 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 (thuộc TestNG)

import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
public class TestListener implements ITestListener {
    @Override
    public void onStart(ITestContext result) {
        // TODO Auto-generated method stub

    }

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

    }

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

    }

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

    }

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

    }

    @Override
    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onTestSkipped(ITestResult result) {
        // 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 test case khi SUCCESSFAIL chẳng hạn:

import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
public class TestListener implements ITestListener {
    @Override
    public void onStart(ITestContext result) {
        // TODO Auto-generated method stub

    }

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

    }

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

    }

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

    }

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

    }

    @Override
    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
        // TODO Auto-generated method stub

    }

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

    }
}

 

🔆 Sử dụng TestListener


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

import com.anhtester.common.BaseTest;
import com.anhtester.globals.TokenGlobal;
import com.anhtester.listeners.TestListener;
import io.restassured.http.ContentType;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import org.testng.Assert;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

import java.io.File;

import static io.restassured.RestAssured.given;

@Listeners(TestListener.class)
public class CategoryTest_Listener extends BaseTest {

    int CATEGORY_ID;

    @Test(priority = 1)
    public void testAddNewCategory() {
        System.out.println("Create Category");
        String dataFile = "src/test/resources/testdata/CreateCategory.json";

        RequestSpecification request = given();
        request.baseUri("https://api.anhtester.com/api")
                .accept(ContentType.JSON)
                .contentType(ContentType.JSON)
                .header("Authorization", "Bearer " + TokenGlobal.TOKEN)
                .body(new File(dataFile));

        Response response = request.post("/category");

        response.prettyPrint();
        response.then().statusCode(200);

        CATEGORY_ID = Integer.parseInt(response.path("response.id").toString());
        System.out.println(CATEGORY_ID);

    }

    @Test(priority = 2)
    public void getCategoryById() {
        System.out.println("Get Category By Id");

        RequestSpecification request = given();
        request.baseUri("https://api.anhtester.com/api")
                .accept(ContentType.JSON)
                .contentType(ContentType.JSON)
                .header("Authorization", "Bearer " + TokenGlobal.TOKEN);

        System.out.println("CATEGORY_ID: " + CATEGORY_ID);
        Response response = request.get("/category/" + CATEGORY_ID);

        response.prettyPrint();
        response.then().statusCode(200);

        JsonPath jsonPath = response.jsonPath();
        Assert.assertEquals(jsonPath.get("response.name"), "Testing Category 05", "The Category Name not match.");

    }
}


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 ở cuối, Syntax: @Listeners(ClassName.class)

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

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

KẾT QUẢ:

Token Global: 3642|6FMRP7NbFHcgwh1Q4QmgsmqG5YFEWuB9ksZHsVw98df8fb16
This is before class CHA
Create Category
{
    "message": "The name has already been taken.",
    "errors": {
        "name": [
            "The name has already been taken."
        ]
    }
}
Đây là test case bị fail: testAddNewCategory

java.lang.AssertionError: 1 expectation failed.
Expected status code <200> but was <422>.


	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
	at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:73)
	at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:108)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:57)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:263)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:277)
	at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure.validate(ResponseSpecificationImpl.groovy:512)
	at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure$validate$1.call(Unknown Source)
	at io.restassured.internal.ResponseSpecificationImpl.validateResponseIfRequired(ResponseSpecificationImpl.groovy:696)
	at io.restassured.internal.ResponseSpecificationImpl.this$2$validateResponseIfRequired(ResponseSpecificationImpl.groovy)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.codehaus.groovy.runtime.callsite.PlainObjectMetaMethodSite.doInvoke(PlainObjectMetaMethodSite.java:43)
	at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:198)
	at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:62)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:185)
	at io.restassured.internal.ResponseSpecificationImpl.statusCode(ResponseSpecificationImpl.groovy:135)
	at io.restassured.specification.ResponseSpecification$statusCode$0.callCurrent(Unknown Source)
	at io.restassured.internal.ResponseSpecificationImpl.statusCode(ResponseSpecificationImpl.groovy:143)
	at io.restassured.internal.ValidatableResponseOptionsImpl.statusCode(ValidatableResponseOptionsImpl.java:89)
	at com.anhtester.Bai15_TestListener.CategoryTest_Listener.testAddNewCategory(CategoryTest_Listener.java:37)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:139)
	at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:664)
	at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:228)
	at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:63)
	at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:961)
	at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:201)
	at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:148)
	at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at org.testng.TestRunner.privateRun(TestRunner.java:819)
	at org.testng.TestRunner.run(TestRunner.java:619)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:443)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:437)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:397)
	at org.testng.SuiteRunner.run(SuiteRunner.java:336)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1301)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1228)
	at org.testng.TestNG.runSuites(TestNG.java:1134)
	at org.testng.TestNG.run(TestNG.java:1101)
	at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:65)
	at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:105)

Get Category By Id
CATEGORY_ID: 0
{
    "message": "Not found",
    "errors": "No categogy found with the submitted id"
}
Đây là test case bị fail: getCategoryById

java.lang.AssertionError: 1 expectation failed.
Expected status code <200> but was <400>.


	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
	at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:73)
	at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:108)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:277)
	at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure.validate(ResponseSpecificationImpl.groovy:512)
	at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure$validate$1.call(Unknown Source)
	at io.restassured.internal.ResponseSpecificationImpl.validateResponseIfRequired(ResponseSpecificationImpl.groovy:696)
	at io.restassured.internal.ResponseSpecificationImpl.this$2$validateResponseIfRequired(ResponseSpecificationImpl.groovy)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.codehaus.groovy.runtime.callsite.PlainObjectMetaMethodSite.doInvoke(PlainObjectMetaMethodSite.java:43)
	at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:198)
	at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:62)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:185)
	at io.restassured.internal.ResponseSpecificationImpl.statusCode(ResponseSpecificationImpl.groovy:135)
	at io.restassured.specification.ResponseSpecification$statusCode$0.callCurrent(Unknown Source)
	at io.restassured.internal.ResponseSpecificationImpl.statusCode(ResponseSpecificationImpl.groovy:143)
	at io.restassured.internal.ValidatableResponseOptionsImpl.statusCode(ValidatableResponseOptionsImpl.java:89)
	at com.anhtester.Bai15_TestListener.CategoryTest_Listener.getCategoryById(CategoryTest_Listener.java:58)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:139)
	at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:664)
	at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:228)
	at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:63)
	at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:961)
	at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:201)
	at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:148)
	at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at org.testng.TestRunner.privateRun(TestRunner.java:819)
	at org.testng.TestRunner.run(TestRunner.java:619)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:443)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:437)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:397)
	at org.testng.SuiteRunner.run(SuiteRunner.java:336)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1301)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1228)
	at org.testng.TestNG.runSuites(TestNG.java:1134)
	at org.testng.TestNG.run(TestNG.java:1101)
	at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:65)
	at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:105)


===============================================
Default Suite
Total tests run: 2, Passes: 0, Failures: 2, Skips: 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ác hàm xử lý chung để thiết lập chạy trước sau hoặc ghi Logs, Reports, Send Mail,... khi test cases FAIL hoặc SUCCESS.


🔆 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"/>
    </listeners>

    <test name="Test">
        <classes>
            <class name="com.anhtester.Bai15_TestListener.CategoryTest_Listener">
            </class>
        </classes>
    </test>
</suite>


Có bao nhiêu class Listeners thì chúng ta gọi vào trong thẻ <listeners> và khai báo với từng thẻ <listener>

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