Nội dung bài học
- Listener trong TestNG là gì?
- Các loại Listeners trong TestNG
- ITestListener trong TestNG
- Làm thế nào để triể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à nó phải đang nghe một thứ gì đó =))
TestNG Listeners là đoạn mã đọ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 class 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 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
- ITestListener
- IReporter
- ISuiteListener
- IInvokedMethod
- IHookable
- IConfigurationListener
- IConfigurableListener
- IAnnotationTransformer
- IExecution
- IMethodInterceptor
Chúng ta sẽ học:
- 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: Chú thích Listeners trên mỗi lớp trong testcase.
- Ở cấp độ Suite: Xác định tên lớp để triển khai trình Listener 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 cơ mà =))
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
public class TestListener implements ITestListener {
@Override
public void onFinish(ITestContext arg0) {
// TODO Auto-generated method stub
}
@Override
public void onStart(ITestContext arg0) {
// TODO Auto-generated method stub
}
@Override
public void onTestFailedButWithinSuccessPercentage(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
}
@Override
public void onTestStart(ITestResult arg0) {
// TODO Auto-generated method stub
}
@Override
public void onTestSuccess(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
Tiếp theo tạo class Test case để gọi lại Listener
Tạo class tên ListenerTC chẵn hạn
package anhtester.com.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 ListenerTC {
WebDriver driver;
@BeforeClass
public void setupDriver() {
WebDriverManager.chromedriver().setup();
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 TC nào
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. Rõ là 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ể quăng cái Capture Screenshot học bài trước vào chổ fail hay pass là đúng bài luôn =))
try {
CaptureHelpers.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 hen.
@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ậy là xong rồi. Tự chạy coi kết quả đi chứ hả =))
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ả nè
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 nhìn mắt trước đó =))
Và nó thông báo câu mà mình đã ghi test thử lấy ra tên case đấy. Ok done nhé !!!
Triển khai ITestListener ở cấp độ Suite
Để triển khai ITestListener ở cấp Suite, thì chúng ta xóa chú thích @Listener khỏi class Test case đi và thêm nó vào tệp XML.
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="Suite">
<listeners>
<listener class-name="anhtester.com.common.TestListener"></listener>
</listeners>
<test name="Test">
<classes>
<class name="anhtester.com.testcases.ListenerTC">
</class>
</classes>
</test>
</suite>
Đấy. Nó chỉ vậy thôi.
Kiểm tra trạng thái Test Case sau khi chạy
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.
Dùng class ITestResult
và kết hợp ghi chú @AfterMethod
để bắt
@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
Chúng ta để nó chung với test case luôn không cần để bên Listener
Này để dùng khi cần check gì đó sau mỗi case chạy như Screenshot chẵn hạn. Kiểu nếu trạng thái fail thì chụp lại.
Anh Tester
facebook.com/anhtester
Đườ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