前言
前面的文章针对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:TestNGHelloWorld1@15f2bb7] 23014327, TestNGHelloWorld1.helloWorldTest1()[pri:0, instance:TestNGHelloWorld1@15f2bb7] 23014327, TestNGHelloWorld1.helloWorldTest2(java.lang.String)[pri:0, instance:TestNGHelloWorld1@15f2bb7]Tom 23014327, TestNGHelloWorld1.AfTest()[pri:0, instance:TestNGHelloWorld1@15f2bb7] 23014327]
获取所有@Test标注的方法:[TestNGHelloWorld1.helloWorldTest1()[pri:0, instance:TestNGHelloWorld1@15f2bb7], TestNGHelloWorld1.helloWorldTest2(java.lang.String)[pri:0, instance:TestNGHelloWorld1@15f2bb7]]
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 文件时,不需要把监听器添加到每个文件中。
网友评论