【TestNG】TestNG自定义监听器大全

前言

TestNG默认有提供监听器,但是如果我们想自定义监听器也是可以的,自定义监听器包括好几种,部分如下所示:

  • IAnnotationTransformer
  • IAnnotationTransformer2
  • IHookable
  • IInvokedMethodListener
  • IMethodInterceptor
  • IReporter
  • ISuiteListener
  • ITestListener

全部监听器如下图所示:
【TestNG】TestNG自定义监听器大全_第1张图片
使用命令行调用TestNG同时添加监听器可以参考如下代码:

java org.testng.TestNG -listener MyTransformer testng.xml

下面介绍下这些:

一、ISuiteLinstener监听器

这个其实不常用,只有两个方法要实现,开始测试套和结束测试套。
实现自定义测试套监听器可以这么写:

package com.demo.test.testng;

import org.testng.ISuite;
import org.testng.ISuiteListener;

public class MySuiteListener implements ISuiteListener{

	public void onStart(ISuite suite) {
		System.out.println("suite onStart!");
	}

	public void onFinish(ISuite suite) {
		System.out.println("suite onFinish!");
	}

}

测试用例如下:

package com.demo.test.testng;

import org.testng.annotations.Test;

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

对应的xml如下,注意里边的那个listener标签,即为上面添加的那个监听器:



<suite name="this suite" parallel="none" verbose="1">
  <listeners>
    <listener class-name="com.demo.test.testng.MySuiteListener" />
  listeners>
  <test name="Test">
    <classes>
      <class name="com.demo.test.testng.ListenerTest"/>
    classes>
  test> 
suite> 

测试结果如下:

[RemoteTestNG] detected TestNG version 6.10.0
[TestNG] Running:
  D:\software\workspace\testng\src\main\java\com\demo\test\testCase\listenerTest1.xml

suite onStart!
test1
test2
suite onFinish!

===============================================
this suite
Total tests run: 2, Failures: 0, Skips: 0
===============================================

实现这个接口的自定义监听器只能通过xml里配置listener来加入;

二、IAlterSuiteListener监听器

这个监听器可以用于获取xml文件对应的XmlSuite,对于我们想根据suite修改一些参数啊之类的东西的需求,可以通过实现此监听器来实现,方法如下:


public class MyAlterSuiteNameListener implements IAlterSuiteListener {
 
    @Override
    public void alter(List<XmlSuite> suites) {
        XmlSuite suite = suites.get(0);//只取第一个suite
        suite.setName(getClass().getSimpleName());
    }

在获取到XmlSuite对象后就可以操作里边的所有内容了,包括配置和测试用例等;
注意:IAlterSuiteListener监听器的使用有特殊要求。使用IAlterSuiteListener监听器,只能通过在testng.xml中的,或者通过Java的SPI接口。除此之外的其他方法,如在测试类中通过@Listeners标注等,都是无效的。

三、ITestListener监听器

这个监听器比较常用了,内含的接口比较多,在我们需要对失败等用例做一些特别操作的时候就需要这个了。多适用于这样一些情况:
当使用testng执行测试时,我们常会想在某个阶段做一些特别的处理,比如:测试成功结束后,测试失败后,跳过某个脚本后,全部脚本执行完毕后。
要想达成这个目标,我们需要实现testng的ITestListener接口,自定义一个自己的listener。
ITestListener包含两种类型的方法:

  • 一类是测试用例级别的,例如onTestStart,onTestSuccess,onTestFailure,onTestSkipped,onTestFailedButWithinSuccessPercentage;
  • 另一类是测试集级别的,例如onStart,onFinish。这些方法有一个输入参数,result或者context。result是ITestResult类型的,可以知晓测试用例成功或者失败和测试何时开始等信息。context是ITestContext类型的,适用于测试集级别,可以知晓成功的脚本有哪些,失败的脚本有哪些。
    范例如下:
    监听器如下:
package com.demo.test.testng;

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

public class MyTestListener implements ITestListener{

	public void onTestStart(ITestResult result) {
		System.out.println("onTestStart");
	}

	public void onTestSuccess(ITestResult result) {
		System.out.println("onTestSuccess");
	}

	public void onTestFailure(ITestResult result) {
		System.out.println("onTestFailure");
	}

	public void onTestSkipped(ITestResult result) {
		System.out.println("onTestSkipped");
	}

	public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
		System.out.println("onTestFailedButWithinSuccessPercentage");
	}

	public void onStart(ITestContext context) {
		System.out.println("onStart");
	}

	public void onFinish(ITestContext context) {
		System.out.println("onFinish");
	}

}

测试用例:

package com.demo.test.testng;

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

@Listeners(MyTestListener.class)
public class ListenerTest {
	
  @Test
  public void test1() {
	  System.out.println("test1");
  }
  
  @Test
  public void test2() {
	  System.out.println("test2");
	  Assert.assertFalse(true);
  }
}

用例运行结果如下:

[RemoteTestNG] detected TestNG version 6.10.0
[TestNG] Running:
  C:\Users\dufei\AppData\Local\Temp\testng-eclipse-719761139\testng-customsuite.xml

onStart
onTestStart
test1
onTestSuccess
onTestStart
test2
onTestFailure
onFinish
PASSED: test1
FAILED: test2
java.lang.AssertionError: expected [false] but found [true]
	at org.testng.Assert.fail(Assert.java:94)
	at org.testng.Assert.failNotEquals(Assert.java:513)
	at org.testng.Assert.assertFalse(Assert.java:63)
	at org.testng.Assert.assertFalse(Assert.java:73)
	at com.demo.test.testng.ListenerTest.test2(ListenerTest.java:18)
	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:104)
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:645)
	at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:851)
	at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1177)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112)
	at org.testng.TestRunner.privateRun(TestRunner.java:756)
	at org.testng.TestRunner.run(TestRunner.java:610)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:387)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:382)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:340)
	at org.testng.SuiteRunner.run(SuiteRunner.java:289)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1293)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1218)
	at org.testng.TestNG.runSuites(TestNG.java:1133)
	at org.testng.TestNG.run(TestNG.java:1104)
	at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:114)
	at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:251)
	at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:77)


===============================================
    Default test
    Tests run: 2, Failures: 1, Skips: 0
===============================================


===============================================
Default suite
Total tests run: 2, Failures: 1, Skips: 0
===============================================

[TestNG] Time taken by org.uncommons.reportng.HTMLReporter@1d7acb34: 62 ms

当然也可以在xml中指定,如下xml:



<suite name="this suite" parallel="none" verbose="1">
  <listeners>
    <listener class-name="com.demo.test.testng.MySuiteListener" />
    <listener class-name="com.demo.test.testng.MyTestListener" />
  listeners>
  <test name="Test">
    <classes>
      <class name="com.demo.test.testng.ListenerTest"/>
    classes>
  test> 
suite> 

运行结果就不贴了,跟上面的那个结果类似。

四、IReporter监听器

这个监听器就是用于自定义报告的,这里我们就可以把结果写入数据库、或者写入xml、html或者EXCEL之类的。

package com.demo.test.testng;

import java.util.List;

import org.testng.IReporter;
import org.testng.ISuite;
import org.testng.xml.XmlSuite;

public class MyReporter implements IReporter{

	public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, 
			String outputDirectory) {
		
	}

}

范例就不写了,这个根据实际需求来做即可;

五、IMethodInterceptor监听器

此监听器用于改变执行顺序等,为一个方法级的监听器,实现自定义监听器的范例如下。
在TestNG的执行过程中,根据测试方法的执行顺序,可以将测试方法分为如下两类:

  • 顺序执行的方法,即依赖于其他测试方法或者被其他测试方法所依赖
  • 执行无特定顺序的方法,即测试方法的执行没有特定的顺序要求,不同的TestNG执行可能有不同的顺序。

对于第一类测试方法,其执行顺序不能随意改变,必须符合依赖关系。但是对于第二类测试方法,TestNG提供了IMethodInterceptor监听器,可以改变这些测试方法的执行顺序。

监听器代码:

package com.demo.test.testng;

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

import org.testng.IMethodInstance;
import org.testng.IMethodInterceptor;
import org.testng.ITestContext;
import org.testng.annotations.Test;


public class MyMethodInterceptor implements IMethodInterceptor
{

	public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) 
	{
		List<IMethodInstance> result = new ArrayList<IMethodInstance>();
		for (IMethodInstance m : methods) 
		{
			Test test = m.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class);
			Set<String> groups = new HashSet<String>();
			for (String group : test.groups()) 
			{
				groups.add(group);
			}
			if (groups.contains("dependGroup1")) 
			{
				result.add(0, m);
			}
			else {
				result.add(m);
			}
		}
		return result;

	}

}

用例代码,内含两个group,正常执行顺序是不能保证的:

package com.demo.test.testng;

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

public class DependTest1 {
	
	@Test(groups= {"dependGroup1"})
	public void dependTest1() 
	{
		System.out.println("dependTest1");
	}
	
	@Test(groups= {"dependGroup1"})
	public void dependTest2() 
	{
		System.out.println("dependTest2");
	}
	
	@Test(groups="dependGroup2")
	public void dependTest4() 
	{
		System.out.println("dependTest4");
		Assert.assertFalse(false);
	}
	
}

执行代码:

package com.demo.test.testng;

import org.testng.TestNG;

public class Entry {

	public static void main(String[] args) {
		TestNG testNG = new TestNG();
		testNG.setVerbose(3);
		testNG.setTestClasses(new Class[]{DependTest1.class});
		testNG.setMethodInterceptor(new MyMethodInterceptor());
		testNG.run();
	}
}

执行结果:

[TestRunner] Running the tests in 'Command line test' with parallel mode:none
[RunInfo] Adding method selector: org.testng.internal.XmlMethodSelector@50134894 priority: 10
[TestClass] Creating TestClass for [ClassImpl class=com.demo.test.testng.DependTest1]
[TestNG] Running:
  Command line suite

[SuiteRunner] Created 1 TestRunners
[TestRunner] Running test Command line test on 1  classes,  included groups:[] excluded groups:[]
===== Test class
com.demo.test.testng.DependTest1
    @Test DependTest1.dependTest1()[pri:0, instance:com.demo.test.testng.DependTest1@73a28541]
    @Test DependTest1.dependTest2()[pri:0, instance:com.demo.test.testng.DependTest1@73a28541]
    @Test DependTest1.dependTest4()[pri:0, instance:com.demo.test.testng.DependTest1@73a28541]
======
[Invoker 2065530879] Invoking com.demo.test.testng.DependTest1.dependTest2
dependTest2
[Invoker 2065530879] Invoking com.demo.test.testng.DependTest1.dependTest1
dependTest1
[Invoker 2065530879] Invoking com.demo.test.testng.DependTest1.dependTest4
dependTest4
===== Invoked methods
    DependTest1.dependTest2()[pri:0, instance:com.demo.test.testng.DependTest1@73a28541] 1940030785
    DependTest1.dependTest1()[pri:0, instance:com.demo.test.testng.DependTest1@73a28541] 1940030785
    DependTest1.dependTest4()[pri:0, instance:com.demo.test.testng.DependTest1@73a28541] 1940030785
=====
[[Utils]] Attempting to create D:\software\workspace\testng\test-output\Command line suite\Command line test.xml
[[Utils]]   Directory D:\software\workspace\testng\test-output\Command line suite exists: true
Creating D:\software\workspace\testng\test-output\Command line suite\Command line test.xml
PASSED: dependTest2
PASSED: dependTest1
PASSED: dependTest4

===============================================
    Command line test
    Tests run: 3, Failures: 0, Skips: 0
===============================================

可以看到是group名称为dependGroup1的group先执行了,与预期一致;

六、IInvokedMethodListener监听器

在TestNG的执行过程中,通过IInvokedMethodListener监听器,能够在调用某个测试方法之前或者之后发出通知。

  • @Test方法
  • Configuration方法,即@Before方法和@After方法

目前,该监听器只支持调用如下测试方法时发出通知:
此监听器有两个接口可以实现,参数稍有不同,如下所示:

package com.demo.test.testng;

import org.testng.IInvokedMethod;
import org.testng.IInvokedMethodListener;
import org.testng.ITestResult;

public class MyIInvokedMethodListener1 implements IInvokedMethodListener{

	public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
		
	}

	public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
		// TODO Auto-generated method stub
		
	}

}

和:

package com.demo.test.testng;

import org.testng.IInvokedMethod;
import org.testng.IInvokedMethodListener2;
import org.testng.ITestContext;
import org.testng.ITestResult;

public class MyIInvokedMethodListener2 implements IInvokedMethodListener2{

	public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
		// TODO Auto-generated method stub
		
	}

	public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
		// TODO Auto-generated method stub
		
	}

	public void beforeInvocation(IInvokedMethod method, ITestResult testResult, ITestContext context) {
		// TODO Auto-generated method stub
		
	}

	public void afterInvocation(IInvokedMethod method, ITestResult testResult, ITestContext context) {
		// TODO Auto-generated method stub
		
	}

}

七、IHookable监听器

如果添加了这个监听器,则此监听器中的run方法被调用并替代所有被@Test注解标识的方法来运行,即TestNG在运行测试用例的时候是调用此监听器的run方法的,run方法内部可以写一些判断来决定是否执行这个用例,譬如用例鉴权认证,例如在运行接口用例的时候实现此接口内部添加对于登录状态的判定等;
源码提供了一个范例如下:

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);
      }
    };
  }

和@Configuration注解一起使用;

八、IAnnotationTransformer监听器

到目前为止(TestNG 6.9.10),TestNG提供了三个IAnnotationTransformer系列的监听器,这3个监听器的目的都是在TestNG执行过程中动态改变测试类中Annotation的参数。其区别在于不同的IAnnotationTransformer监听器,能够操作的Annotation不同,具体情况如下:

  • IAnnotationTransformer,操作@Test标注
  • IAnnotationTransformer2,操作@Configuration标注、@DataProvider标注和@Factory标注
  • IAnnotationTransformer3,操作@Listeners标注
    三个接口实现如下:
package com.demo.test.testng;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import org.testng.annotations.ITestAnnotation;
import org.testng.internal.annotations.IAnnotationTransformer;

public class MyIAnnotationTransformer1 implements IAnnotationTransformer{

	public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
		annotation.setAlwaysRun(true);
		testClass.isAnnotation();
		testConstructor.getName();
		testMethod.getName();
	}

}
package com.demo.test.testng;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import org.testng.IAnnotationTransformer2;
import org.testng.annotations.IConfigurationAnnotation;
import org.testng.annotations.IDataProviderAnnotation;
import org.testng.annotations.IFactoryAnnotation;
import org.testng.annotations.ITestAnnotation;

public class MyIAnnotationTransformer2 implements IAnnotationTransformer2{

	public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
		// TODO Auto-generated method stub
		
	}

	public void transform(IConfigurationAnnotation annotation, Class testClass, Constructor testConstructor,
			Method testMethod) {
		// TODO Auto-generated method stub
		
	}

	public void transform(IDataProviderAnnotation annotation, Method method) {
		// TODO Auto-generated method stub
		
	}

	public void transform(IFactoryAnnotation annotation, Method method) {
		// TODO Auto-generated method stub
		
	}

}

package com.demo.test.testng;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import org.testng.IAnnotationTransformer3;
import org.testng.annotations.IConfigurationAnnotation;
import org.testng.annotations.IDataProviderAnnotation;
import org.testng.annotations.IFactoryAnnotation;
import org.testng.annotations.IListenersAnnotation;
import org.testng.annotations.ITestAnnotation;

public class MyIAnnotationTransformer3 implements IAnnotationTransformer3{

	public void transform(IConfigurationAnnotation annotation, Class testClass, Constructor testConstructor,
			Method testMethod) {
		// TODO Auto-generated method stub
		
	}

	public void transform(IDataProviderAnnotation annotation, Method method) {
		// TODO Auto-generated method stub
		
	}

	public void transform(IFactoryAnnotation annotation, Method method) {
		// TODO Auto-generated method stub
		
	}

	public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
		// TODO Auto-generated method stub
		
	}

	public void transform(IListenersAnnotation annotation, Class testClass) {
		// TODO Auto-generated method stub
		
	}

}

你可能感兴趣的:(TestNG)