1 概述
监听(Listeners),捕捉Testng的行为,并支持修改,用于定制化,如日志输出、自定义报告
监听器如下:
- IAnnotationTransformer,只支持@Test注解转换
- IAnnotationTransformer2,支持@Test,@DataProvider,@Factory等注解转换,比第一代更全面
- IHookable,替代@Test测试方法,并提供回调函数,常用于权限校验
- IInvokedMethodListener/IInvokedMethodListener2,监听before/after形为,2代增加上下文参数
- IMethodInterceptor,拦截器,调整测试方法的执行顺序
- IReporter,suit执行完成后生成报告
- ISuiteListener,测试套件执行监听
- ITestListener,测试方法执行监听
2 IAnnotationTransformer
实现IAnnotationTransformer的transform方法
参数含义:
- ITestAnnotation,即@Test注解本身,可以修改它的各种属性,如dataProvider,enabled,invocationCount等
- Class,当@Test应用于class时,指向当前类;否则值为null
- Constructor,同上,作用对象为构造器
- Method,同上,作用对象为方法
需求:以2结尾的方法执行2次
public class MyTransformer implements IAnnotationTransformer {
@Override
public void transform(ITestAnnotation iTestAnnotation, Class aClass, Constructor constructor, Method method) {
if (method.getName().endsWith("2")) {
iTestAnnotation.setInvocationCount(2);
}
}
}
public class App {
@Test
public void test_1() {
System.out.println("Just run once");
}
@Test
public void test_2() {
System.out.println("Run twice");
}
}
---------------------------
Just run once
Run twice
Run twice
testng.xml配置
@Listeners不支持IAnnotationTransformer 和 IAnnotationTransformer2,所以只能在testng.xml上配置监听器,当然命令行也可行
3 IHookable
实现IHookable的run方法
调用IHookCallBack的runTestMethod方法,可以回调原测试方法
需求:只执行以2结尾的方法
public class MyHookable implements IHookable {
@Override
public void run(IHookCallBack callBack, ITestResult testResult) {
if (testResult.getMethod().getMethodName().endsWith("2")) {
callBack.runTestMethod(testResult);
}
}
}
@Listeners({MyHookable.class})
public class App {
@Test
public void test_1() {
System.out.println("Just run once");
}
@Test
public void test_2() {
System.out.println("Run twice");
}
}
-----------------------------
Run twice
4 IInvokedMethodListener
适用于configuration(suit, test, class),以及test
配合log4j,可以记录详细的执行过程
public class MyInvokedMethodListener implements IInvokedMethodListener {
@Override
public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
System.out.println("begin to run " + testResult.getName());
}
@Override
public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
System.out.println("end to run " + testResult.getName() + ", result code is " + testResult.getStatus());
}
}
@Listeners({MyInvokedMethodListener.class})
public class App {
@BeforeClass
public void setup() {
System.out.println("initial env");
}
@Test
public void test_1() {
System.out.println("test 1");
Assert.assertTrue(true);
}
@Test
public void test_2() {
System.out.println("test 2");
Assert.assertTrue(false);
}
}
---------------------
begin to run setup
initial env
end to run setup, result code is 1
begin to run test_1
test 1
end to run test_1, result code is 1
begin to run test_2
test 2
end to run test_2, result code is 2
5 IMethodInterceptor
需求:优先执行fast组成员方法
public class MyMethodInterceptor implements IMethodInterceptor {
@Override
public List intercept(List methods, ITestContext context) {
List result = new ArrayList<>();
methods.forEach(method -> {
Test test = method.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class);
Set groups = new HashSet<>();
Arrays.stream(test.groups()).forEach(group -> groups.add(group));
if (groups.contains("fast")) {
result.add(0, method);
} else {
result.add(method);
}
});
return result;
}
}
public class App {
@Test
public void test_1() {
System.out.println("first to run");
}
@Test(groups = {"fast"})
public void test_2() {
System.out.println("second to run");
}
@Test void test_3() {
System.out.println("third to run");
}
}
---------------------------------
second to run
first to run
third to run
6 ISuiteListener
覆写onStart 和 onFinish方法,分别对应suite开始阶段和结束阶段
7 ITestListener
对应测试方法的7种状态:
- onStart,测试类实例化后执行
- onFinish,测试类下的所有测试方法完成后执行
- onTestStart,每次执行测试方法前执行
- onTestSuccess,每次测试方法执行成功后执行
- onTestFailure,每次测试方法执行失败后执行
- onTestSkipped,每次测试方法跳过时执行
- onTestFailedButWithinSuccessPercentage,每次测试方法执行失败,但正确率符合successPercentage要求
实现ITestListener接口,需要覆写上述7种方法,比较麻烦
因此实际偏向于继承TestListenerAdapter
7 IReporter
获取testng执行结果,与html模板结合可以定制测试报告
public class MyReporter implements IReporter {
@Override
public void generateReport(List xmlSuites, List suites, String outputDirectory) {
suites.forEach(suite -> {
System.out.println("测试方法如下:");
suite.getAllMethods().forEach(iTestNGMethod -> System.out.println("\t" + iTestNGMethod.getMethodName()));
System.out.println("报告输出路径:" + suite.getOutputDirectory());
suite.getResults().values().forEach(iSuiteResult -> {
ITestContext iTestContext = iSuiteResult.getTestContext();
Set passedTests = iTestContext.getPassedTests().getAllResults();
System.out.println("执行成功的方法:" + passedTests.size());
passedTests.forEach(iTestResult -> {
System.out.println("\t" + iTestResult.getName());
System.out.println("\t\t开始时间:" + iTestResult.getStartMillis());
System.out.println("\t\t结束时间:" + iTestResult.getEndMillis());
});
Set failedTests = iTestContext.getFailedTests().getAllResults();
System.out.println("执行失败的方法:" + failedTests.size());
failedTests.forEach(iTestResult -> {
System.out.println("\t" + iTestResult.getName());
System.out.println("\t\t开始时间:" + iTestResult.getStartMillis());
System.out.println("\t\t结束时间:" + iTestResult.getEndMillis());
});
});
});
}
}
@Listeners({MyReporter.class})
public class App {
@Test
public void test_1() {
System.out.println("first to run");
}
@Test
public void test_2() {
System.out.println("second to run");
}
@Test
public void test_3() {
Assert.assertTrue(false);
}
}
------------------------
测试方法如下:
test_1
test_2
test_3
报告输出路径:E:\code\java\lab\test-output\Default Suite
执行成功的方法:2
test_2
开始时间:1541921094204
结束时间:1541921094204
test_1
开始时间:1541921094188
结束时间:1541921094188
执行失败的方法:1
test_3
开始时间:1541921094204
结束时间:1541921094204