最近忙着折腾 TestNG 项目,顺便整理记录一下。
记得几年前去某名企面试,我说我们在折腾平台,人家说平台有卵用,TestNG 就可以了。。。那么,TestNG 有什么优点呢?
- 支持 Java 注释功能
- 测试运行在任意大的线程池中,并且有多种运行策略可供选择(所有测试方法运行在自己的线程中、每个测试类一个线程,等等)。
- 线程安全
- 灵活的测试配置
- 支持数据驱动测试(通过 @DataProvider 注释)
- 支持参数化
- 强大的运行模型(不再使用 TestSuite)
- 有多种工具和插件支持(Eclipse, IDEA, Maven, 等等)
- 内嵌 BeanShell 以进一步增强灵活性
- 默认提供 JDK 的运行时和日志功能
- 提供应用服务器测试依赖的方法
TestNG 提供的几种监听器
- IAnnotationTransformer 用来修改 @Test 注释(例annotation.setEnabled(false);)
- IAnnotationTransformer2 在运行时修改除 @Test 以外的 TestNG 的注释 (@Configuration,@DataProvider 以及 @Factory 不过不推荐。建议使用 @BeforeSuite,@AfterSuite 代替)
- IHookable 类似与面向方面编程(AOP)中的 Around Advice 的功能。它在测试方法执行前后提供了切入点。常用于测试方法执行前授权检查
- IInvokedMethodListener 类似与面向方面编程(AOP)中的 Before Advice 和 After Advice,允许用户在当前测试方法被执行前和执行后注入特定的逻辑,比如,加日志。
- IMethodInterceptor 可以用来重新排序、甚至增加、减少测试方法
- IReporter 定制不同格式报表
- ISuiteListener 在 Suite 之前、之前嵌入自定义逻辑
- ITestListener 在测试方法执行成功、失败或者跳过时指定不同后续行为
监听器使用方法
testng.xml 中使用
1 2 3 4 5
| <suite name="xxx"> <listeners> <listener class-name="com.tracenote.XXXListener"/> </listeners> </suite>
|
源代码中使用
1 2 3 4
| @Listeners({ XXXListener.class }) public class HiTest {
}
|
@Listeners 中添加监听器跟在 testng.xml 添加监听器的不同之处在于,它不能添加 IAnnotationTransformer 和 IAnnotationTransformer2 监听器。原因是因为这两种监听器必须在更早的阶段添加到 TestNG 中才能实施修改注释的操作,所以它们只能在 testng.xml 添加。
通过 ServiceLoader 使用
Java SE 6 开始提供了 ServiceLoader。它可以帮助用户查找、加载和使用服务提供程序,从而在无需修改原有代码的情况下轻易地扩展目标应用程序。通过 ServiceLoader 的方式使用 TestNG 监听器,简单来说,就是创建一个 jar 文件,里面包含 TestNG 监听器的实现类已经 ServiceLoader 需要的配置信息,并在运行 TestNG 时把该 jar 文件加载到类路径中。
通过命令行使用
加参数 -listener MyListener,XXXListener
监听器简单示例
IReporter
在测试方法执行后执行,通过遍历 xmlSuites 和 suites 能够获取所有测试方法的信息以及测试结果,后续可用于自定义测试报告。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public class IReporterImp implements IReporter { @Override public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> iSuites, String outputDirectory) { 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("报告路径:"+outputDirectory); System.out.println("并发方式:"+iSuite.getParallel()); for(ISuiteResult iSuiteResult:iSuiteResultMap.values()){ ITestContext iTestContext = iSuiteResult.getTestContext(); IResultMap iResultMap = iTestContext.getPassedTests(); 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()); } } } } }
|
IHookable
常用于测试方法执行前执行授权检查
1 2 3 4 5 6 7 8 9 10 11 12
| public class IHookableImp implements IHookable { @Override public void run(IHookCallBack iHookCallBack, ITestResult iTestResult) { ConstructorOrMethod method = iTestResult.getMethod().getConstructorOrMethod(); String name = method.getName(); System.out.println("测试method是 "+name); System.out.println("开始执行~"); iHookCallBack.runTestMethod(iTestResult); System.out.println("结束~"); } }
|
IInvokedMethodListener
常用于日志的采集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class IInvokedMethodListenerImp implements IInvokedMethodListener { @Override public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) { System.out.println(iTestResult.getName()); } @Override public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) { System.out.println(iTestResult.getName()); } }
|
TestListenerAdapter
TestListenerAdapter 已经实现 ITestListener,并且提供了一些有用的方法,比如分别获取所有成功失败跳过三种测试结果的测试方法的方法,并且 ITestListner 中有很多方法而 TestListenerAdapter 已给出了默认实现。因此,继承 TestListenerAdapter 后,便只需关注需要修改的方法。
用途举例,执行测试方法后执行,记录log信息,根据执行结果的不同调用不同的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class TestListenerAdapterImp extends TestListenerAdapter { private int m_count = 0; @Override public void onTestFailure(ITestResult tr) { log(tr.getName()+ "--Test method failed\n"); } @Override public void onTestSkipped(ITestResult tr) { log(tr.getName()+ "--Test method skipped\n"); } @Override public void onTestSuccess(ITestResult tr) { log(tr.getName()+ "--Test method success\n"); } private void log(String string) { System.out.print(string); if (++m_count % 40 == 0) { System.out.println(""); } } }
|
Reporter
使用 org.testng.Reporter
,可以实现直接把信息写入 HTML 报告。
当然,比较常见的是用 ReportNG 替代 TestNG 默认报告。有时间再另外说说。