欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

(十四)TestNG学习之路—TestNG监听器

程序员文章站 2022-05-17 07:57:48
...

目录

(一)TestNG学习之路—HelloWorld入门
(二)TestNG学习之路—注解及属性概览
(三)TestNG学习之路—TestNG.xml/YAML
(四)TestNG学习之路—注解详述之@Test
(五)TestNG学习之路—注解详述之参数化
(六)TestNG学习之路—注解详述之@Factory
(七)TestNG学习之路—注解详述之忽略测试
(八)TestNG学习之路—注解详述之并发
(九)TestNG学习之路—失败测试重跑
(十)TestNG学习之路—编码执行TestNG
(十一)TestNG学习之路—BeanShell高级用法
(十二)TestNG学习之路—注解转换器
(十三)TestNG学习之路—方法拦截器
(十四)TestNG学习之路—TestNG监听器
(十五)TestNG学习之路—依赖注入
(十六)TestNG学习之路—测试报告
(十七)基于TestNG+Rest Assured+Allure的接口自动化测试框架

前言

前面的文章针对IAnnotationTransformer,IAnnotationTransformer2,IMethodInterceptor几种监听器做了举例说明,该篇文章咱们接着再探讨几种常见的监听器,更多的监听器请访问javadoc

监听器

IHookable
public interface IHookable extends ITestNGListener {
    void run(IHookCallBack var1, ITestResult var2);
}

IHookable接口继承自ITestNGListener接口,其定义了唯一的run方法。如javadoc文档所述,它在测试方法执行前提供了切入点,根据当前执行的情况决定是否执行某个测试方法,典型应用是执行测试方法前进行授权检查,根据授权结果执行测试,官网举例如下:

public class MyHook implements IHookable {
  public void run(final IHookCallBack icb, ITestResult testResult) {
    // Preferably initialized in a @Configuration method
    mySubject = authenticateWithJAAs();
    
    Subject.doAs(mySubject, new PrivilegedExceptionAction() {
      public Object run() {
        icb.callback(testResult);
      }
    };
  }
}

一个简单的例子如下。
编写IHookable的实现类,简单输出“tom”。

import org.testng.IHookCallBack;
import org.testng.IHookable;
import org.testng.ITestResult;

public class IHookableImp implements IHookable {
    @Override
    public void run(IHookCallBack iHookCallBack, ITestResult iTestResult) {
        System.out.println("tom");
        iHookCallBack.runTestMethod(iTestResult);
    }
}

编写测试类如下:

import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

@Listeners(IHookableImp.class)
public class IHookableImpTest {
    @Test
    public void test(){
        System.out.println("test");
    }
}

执行结果:

tom
test

===============================================
Default Suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================
IInvokedMethodListener
public interface IInvokedMethodListener extends ITestNGListener {
    void beforeInvocation(IInvokedMethod var1, ITestResult var2);

    void afterInvocation(IInvokedMethod var1, ITestResult var2);
}

IInvokedMethodListener接口继承自ITestNGListener接口,其定义了beforeInvocation和afterInvocation方法。TestNG在调用方法前、后启用该监听器,常用于日志的采集。举例如下:
编写IInvokedMethodListenerImp实现类:

import org.testng.*;

public class IInvokedMethodListenerImp implements IInvokedMethodListener {

    @Override
    public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
        System.out.println("beforeInvocation:"+iTestResult.getName());
    }

    @Override
    public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
        System.out.println("afterInvocation:"+iTestResult.getName());
    }
}

编写测试类:

import org.testng.annotations.BeforeClass;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

@Listeners(IInvokedMethodListenerImp.class)
public class IInvokedMethodListenerImpTest {

    @BeforeClass
    public void bfClass(){
        System.out.println("bfClass123");
    }

    @Test
    public void test(){
        System.out.println("test123");
    }
}

执行结果如下:

beforeInvocation:bfClass
bfClass123
afterInvocation:bfClass
beforeInvocation:test
test123
afterInvocation:test

===============================================
Default Suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================

如上结果所示,所有被注解的方法执行前都先执行监听器的beforeInvocation方法,执行后都执行afterInvocation方法。
另外,TestNG还提供了IInvokedMethodListener2监听器,其中定义的方法加入用户信息的参数,使用起来更灵活。

public interface IInvokedMethodListener2 extends IInvokedMethodListener {
    void beforeInvocation(IInvokedMethod var1, ITestResult var2, ITestContext var3);

    void afterInvocation(IInvokedMethod var1, ITestResult var2, ITestContext var3);
}
IReporter
public interface IReporter extends ITestNGListener {
    void generateReport(List<XmlSuite> var1, List<ISuite> var2, String outputDirectory);
}

IReporter接口继承自ITestNGListener接口,其定义了generateReport方法。TestNG在运行所有套件时都将调用此方法,通过遍历 xmlSuites 和 suites 能够获取所有测试方法的信息以及测试结果,后续可用于自定义测试报告,示例如下:
编写IReporter实现类:

import org.testng.*;
import org.testng.xml.XmlSuite;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class IReporterImp implements IReporter {
    @Override
    public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> iSuites, String outputDirectory) {

        ArrayList<String> list = new ArrayList<String>();
        for(ISuite iSuite:iSuites){
            Map<String,ISuiteResult> iSuiteResultMap = iSuite.getResults();
            System.out.println("所有执行的方法:"+iSuite.getAllInvokedMethods());
            System.out.println("获取所有@Test标注的方法:"+iSuite.getAllMethods());
            System.out.println("suiteName:"+iSuite.getName());
            System.out.println("输出路径:"+iSuite.getOutputDirectory());
            System.out.println("并发方式:"+iSuite.getParallel());
            System.out.println("参数tom的值:"+iSuite.getParameter("tom"));
            System.out.println("报告路径:"+outputDirectory);

            for(ISuiteResult iSuiteResult:iSuiteResultMap.values()){
                ITestContext iTestContext = iSuiteResult.getTestContext();
                IResultMap iResultMap = iTestContext.getPassedTests();
                IResultMap iResultMap1 = iTestContext.getFailedTests();

                Set<ITestResult> iTestResultset = iResultMap.getAllResults();
                for(ITestResult iTestResult:iTestResultset){
                    System.out.println("测试方法:"+iTestResult.getName());
                    System.out.println("执行结果(1-成功,2-失败,3-skip):"+iTestResult.getStatus());
                    System.out.println("开始时间:"+iTestResult.getStartMillis());
                    System.out.println("结束时间:"+iTestResult.getEndMillis());
                }

                Set<ITestResult> iTestResultset1 = iResultMap1.getAllResults();
                for(ITestResult iTestResult1:iTestResultset1){
                    System.out.println("测试方法:"+iTestResult1.getName());
                    System.out.println("执行结果(1-成功,2-失败,3-skip):"+iTestResult1.getStatus());
                    System.out.println("开始时间:"+iTestResult1.getStartMillis());
                    System.out.println("结束时间:"+iTestResult1.getEndMillis());
                }
            }
        }
    }
}

编写测试类:

import org.testng.Assert;
import org.testng.annotations.*;

@Test(groups = "test1")
public class TestNGHelloWorld1 {
    @BeforeTest
    public void bfTest() {
        System.out.println("TestNGHelloWorld1 beforTest!");
    }

    @Test(expectedExceptions = ArithmeticException.class, expectedExceptionsMessageRegExp = ".*zero")
    public void helloWorldTest1() {
        System.out.println("TestNGHelloWorld1 Test1!");
        int c = 1 / 0;
        Assert.assertEquals("1", "1");
    }

    @Test()
    @Parameters(value = "para")
    public void helloWorldTest2(@Optional("Tom")String str) {
        Assert.assertEquals("1", "2");
        System.out.println("TestNGHelloWorld1 Test2! "+ str);
    }

    @AfterTest
    public void AfTest() {
        System.out.println("TestNGHelloWorld1 AfterTest!");
    }
}

配置testng.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="All Test Suite" parallel="classes">
    <listeners>
        <listener class-name="IReporterImp"/>
    </listeners>

    <parameter name="tom" value="Tomandy"/>

    <test verbose="2" preserve-order="true" name="Test">
        <classes>
            <class name="TestNGHelloWorld1">
            </class>
        </classes>
    </test>
</suite>

执行结果如下:

TestNGHelloWorld1 beforTest!
TestNGHelloWorld1 Test1!

java.lang.AssertionError: expected [2] but found [1]
Expected :2
Actual   :1
 <Click to see difference>


    at org.testng.Assert.fail(Assert.java:93)
    at org.testng.Assert.failNotEquals(Assert.java:512)
    at org.testng.Assert.assertEqualsImpl(Assert.java:134)
    at org.testng.Assert.assertEquals(Assert.java:115)
    at org.testng.Assert.assertEquals(Assert.java:189)
    at org.testng.Assert.assertEquals(Assert.java:199)
    at TestNGHelloWorld1.helloWorldTest2(TestNGHelloWorld1.java:21)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:108)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:661)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:869)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1193)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:126)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

TestNGHelloWorld1 AfterTest!

===============================================
All Test Suite
Total tests run: 2, Failures: 1, Skips: 0
===============================================

所有执行的方法:[TestNGHelloWorld1.bfTest()[pri:0, instance:[email protected]] 23014327, TestNGHelloWorld1.helloWorldTest1()[pri:0, instance:[email protected]] 23014327, TestNGHelloWorld1.helloWorldTest2(java.lang.String)[pri:0, instance:[email protected]]Tom  23014327, TestNGHelloWorld1.AfTest()[pri:0, instance:[email protected]] 23014327]
获取所有@Test标注的方法:[TestNGHelloWorld1.helloWorldTest1()[pri:0, instance:[email protected]], TestNGHelloWorld1.helloWorldTest2(java.lang.String)[pri:0, instance:[email protected]]]
suiteName:All Test Suite
输出路径:D:\IntelliJ_IDEA_workspace\TestNG\test-output\All Test Suite
并发方式:classes
参数tom的值:Tomandy
报告路径:test-output
测试方法:helloWorldTest1
执行结果(1-成功,2-失败,3-skip):1
开始时间:1536288995670
结束时间:1536288995670
测试方法:helloWorldTest2
执行结果(1-成功,2-失败,3-skip):2
开始时间:1536288995675
结束时间:1536288995679
ISuiteListener
public interface ISuiteListener extends ITestNGListener {
    void onStart(ISuite var1);

    void onFinish(ISuite var1);
}

ISuiteListener接口继承自ITestNGListener接口,类似于 IInvokedMethodListener,用户可通过ISuiteListener监听器,在测试套件执行前或执行后嵌入相关逻辑。
由于跟IInvokedMethodListener类似,此处不再举例。

ITestListener
public interface ITestListener extends ITestNGListener {
   //每次调用测试之前都会调用
    void onTestStart(ITestResult var1);
   //每次测试成功时调用。
    void onTestSuccess(ITestResult var1);
   //每次测试失败时调用。
    void onTestFailure(ITestResult var1);
   //每次测试跳过时调用。
    void onTestSkipped(ITestResult var1);
   //执行测试失败且 successPercentage属性满足条件是调用
    void onTestFailedButWithinSuccessPercentage(ITestResult var1);
   //在实例化测试类之后和在调用任何配置方法之前调用。
    void onStart(ITestContext var1);
   //在运行所有测试并调用所有配置方法之后调用。
    void onFinish(ITestContext var1);
}

ITestListener 接口继承自ITestNGListener接口,定义的方法如上所示。但在实际应用过程中,我们一般使用TestListenerAdapter,因为ITestListner中的方法在TestListenerAdapter中给了默认实现,我们只需继承 TestListenerAdapter,重写自己感兴趣的方法即可,示例如下:
编写TestListenerAdapter子类,重写onTestFailure,onTestSkipped,onTestSuccess方法:

import org.testng.ITestResult;
import org.testng.TestListenerAdapter;

import static org.testng.Reporter.log;

public class TestListenerAdapterImp extends TestListenerAdapter {
    @Override
    public void onTestFailure(ITestResult tr) {
        System.out.println("Failure");
    }

    @Override
    public void onTestSkipped(ITestResult tr) {
        System.out.println("Skip");
    }

    @Override
    public void onTestSuccess(ITestResult tr) {
        System.out.println("Success");
    }

}

编写测试类:

import org.testng.Assert;
import org.testng.annotations.*;

@Test(groups = "test1")
public class TestNGHelloWorld1 {
    @BeforeTest
    public void bfTest() {
        System.out.println("TestNGHelloWorld1 beforTest!");
    }

    @Test(expectedExceptions = ArithmeticException.class, expectedExceptionsMessageRegExp = ".*zero")
    public void helloWorldTest1() {
        System.out.println("TestNGHelloWorld1 Test1!");
        int c = 1 / 0;
        Assert.assertEquals("1", "1");
    }

    @Test()
    @Parameters(value = "para")
    public void helloWorldTest2(@Optional("Tom")String str) {
        Assert.assertEquals("1", "2");
        System.out.println("TestNGHelloWorld1 Test2! "+ str);
    }

    @AfterTest
    public void AfTest() {
        System.out.println("TestNGHelloWorld1 AfterTest!");
    }
}

配置testng.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="All Test Suite" parallel="classes">
    <listeners>
        <listener class-name="TestListenerAdapterImp"/>
    </listeners>

    <parameter name="tom" value="Tomandy"/>

    <test verbose="2" preserve-order="true" name="Test">
        <classes>
            <class name="TestNGHelloWorld1">
            </class>
        </classes>
    </test>
</suite>

执行结果如下:

TestNGHelloWorld1 beforTest!
TestNGHelloWorld1 Test1!
Success

java.lang.AssertionError: expected [2] but found [1]
Expected :2
Actual   :1
 <Click to see difference>


    at org.testng.Assert.fail(Assert.java:93)
    at org.testng.Assert.failNotEquals(Assert.java:512)
    at org.testng.Assert.assertEqualsImpl(Assert.java:134)
    at org.testng.Assert.assertEquals(Assert.java:115)
    at org.testng.Assert.assertEquals(Assert.java:189)
    at org.testng.Assert.assertEquals(Assert.java:199)
    at TestNGHelloWorld1.helloWorldTest2(TestNGHelloWorld1.java:21)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:108)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:661)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:869)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1193)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:126)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Failure
TestNGHelloWorld1 AfterTest!

===============================================
All Test Suite
Total tests run: 2, Failures: 1, Skips: 0
===============================================

如上执行结果所示,每次执行失败或成功都能被监听器捕获。

@Listeners作用范围控制

类似testng.xml添加的listener一样,@Listeners注解作用于整个suite文件,可以通过以下例子来验证。
编写TestListenerAdapter子类,重写onTestFailure,onTestSkipped,onTestSuccess方法:

import org.testng.ITestResult;
import org.testng.TestListenerAdapter;

import static org.testng.Reporter.log;

public class TestListenerAdapterImp extends TestListenerAdapter {
    @Override
    public void onTestFailure(ITestResult tr) {
        System.out.println("Failure");
    }

    @Override
    public void onTestSkipped(ITestResult tr) {
        System.out.println("Skip");
    }

    @Override
    public void onTestSuccess(ITestResult tr) {
        System.out.println("Success");
    }

}

编写测试类ListenerTest,为添加@Listeners注解:

import org.testng.annotations.Test;

public class ListenerTest {
    @Test
    public void Ltest(){
        System.out.println("ListenerTest @Test");
    }
}

编写测试类ListenerTest1,添加@Listeners注解:

import org.testng.Assert;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

@Listeners(TestListenerAdapterImp.class)
public class ListenerTest1 {
    @Test
    public void Ltest1(){
        System.out.println("ListenerTest1 @Test");
    }

    @Test
    public void Ltest2(){
        Assert.assertEquals("1","2");
    }
}

配置testng.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="All Test Suite" parallel="classes">

    <test verbose="2" preserve-order="true" name="Test">
        <classes>
            <class name="ListenerTest"/>
            <class name="ListenerTest1"/>
        </classes>
    </test>
</suite>

执行结果:

ListenerTest1 @Test
ListenerTest @Test
Success
Success
Failure

java.lang.AssertionError: expected [2] but found [1]
Expected :2
Actual   :1
 <Click to see difference>


    at org.testng.Assert.fail(Assert.java:93)
    at org.testng.Assert.failNotEquals(Assert.java:512)
    at org.testng.Assert.assertEqualsImpl(Assert.java:134)
    at org.testng.Assert.assertEquals(Assert.java:115)
    at org.testng.Assert.assertEquals(Assert.java:189)
    at org.testng.Assert.assertEquals(Assert.java:199)
    at ListenerTest1.Ltest2(ListenerTest1.java:14)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:108)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:661)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:869)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1193)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:126)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)


===============================================
All Test Suite
Total tests run: 3, Failures: 1, Skips: 0
===============================================

如果结果所示,ListenerTest类的测试方法也被监听器捕获,尽管该类未添加@Listeners注解,那么有没有方法来限制@Listeners的作用范围呢?答案是有,使用者可以在监听器类中编写判断逻辑实现,官网亦给出了相关的实现示例

监听器引用方式

命令行方式、ant、xml文件、@Listeners注解

以上几种方式前面的文章都已覆盖,不再详述。

ServiceLoader

JDK提供了一种非常优雅的机制,可以通过 ServiceLoader查找、加载和使用服务提供程序,从而在无需修改原有代码的情况下轻易地扩展目标应用程序(类似于Jmeter的jar扩展功能)。对于ServiceLoader,您所需要做的就是创建一个jar文件,其中包含侦听器和一些配置文件,在运行TestNG时将该jar文件放到classpath中,TestNG会自动找到它们,详细操作步骤参考官网。使用该种方式有以下好处。

  • 共享监听器。
  • 当有很多 testng.xml 文件时,不需要把监听器添加到每个文件中。

扩展学习资料

TestNg的IReporter接口的使用
实战 TestNG 监听器