写点什么

TestNG 测试用例重跑详解及实践优化

  • 2020-05-26
  • 本文字数:4369 字

    阅读完需:约 14 分钟

TestNG测试用例重跑详解及实践优化

测试用例运行稳定性是自动化质量的一个重要指标,在运行中需要尽可能的剔除非 bug 造成的测试用例执行失败,对于失败用例进行重跑是常用策略之一。一种重跑策略是所有用例运行结束后对失败用例重跑,另一种重跑策略是在运行时监控用例运行状态,失败后实时重跑。


下面,详细介绍 TestNG 如何对失败测试用例实时重跑并解决重跑过程中所遇到问题的实践和解决方案。对失败测试用例进行实时重跑,有以下几个方面需求:


  1. 测试用例运行失败,监听到失败后立即进行重跑

  2. 测试用例通过 dependsOnMethods/dependsOnGroups 标记依赖其他测试用例,在被依赖的测试用例重跑运行成功后,该测试用例可以继续运行

  3. 对于重跑多次的测试用例,只记录最后一次运行成功或失败结果

一、测试用例重跑

1.1 retryAnalyzer 注解方式

对于希望测试用例中的少量易失败,不稳定的测试用例进行重跑,可采用这种方式。

1.1.1 原理

以下是 TestNG 处理测试用例运行结果的部分代码。


IRetryAnalyzer retryAnalyzer = testMethod.getRetryAnalyzer();boolean willRetry = retryAnalyzer != null && status == ITestResult.FAILURE && failure.instances != null && retryAnalyzer.retry(testResult);if (willRetry) {  resultsToRetry.add(testResult);  failure.count++;  failure.instances.add(testResult.getInstance());  testResult.setStatus(ITestResult.SKIP);} else {  testResult.setStatus(status);  if (status == ITestResult.FAILURE && !handled) {    handleException(ite, testMethod, testResult, failure.count++);  }
复制代码


分析以上代码,其中,接口 IretryAnalyzer 的方法 retry()的返回值作为是否对失败测试用例进行重跑的一个条件。如果 retry()结果为 true,则该失败测试用例会重跑,同时将本次失败结果修改为 Skip;如果结果为 false,则失败的测试用例保持失败结果,运行结束。因此,如果你希望失败测试用例重跑的话,需要把 IretryAnalyzer 的 retry()方法重写,插入自己定义的逻辑,设置返回值为 true。

1.1.2 代码

创建类 RetryImpl,重写 retry()方法,设置失败测试用例的重跑次数,代码如下:


public class RetryImpl implements IRetryAnalyzer {    private int count = 1;    private int max_count = 3;   // Failed test cases could be run 3 times at most    @Override    public boolean retry(ITestResult result) {        System.out.println("Test case :"+result.getName()+",retry time: "+count+"");        if (count < max_count) {            count++;            return true;        }        return false;    }}
复制代码

1.1.3 实例

public class TestNGReRunDemo {    @Test(retryAnalyzer=RetryImpl.class)    public void test01(){        Assert.assertEquals("success","fail");        System.out.println("test01");    }}
复制代码


以上测试用例 test01 可重复运行 3 次。

1.2 实现接口 IAnnotationTransformer 方式

如果希望所有失败的测试用例都进行重跑,采用 retryAnalyzer 注解方式对每个测试用例进行注解就比较麻烦。通过实现 IAnnotationTransformer 接口的方式,可以对全量测试用例的重试类进行设置。该接口是一个监听器接口,用来修改 TestNG 注解。IAnnotationTransformer 监听器接口只有一个方法:transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod). 上文中,我们自定义了类 RetryImpl 实现接口 IRetryAnalyzer。TestNG 通过 transfrom()方法修改 retryAnalyzer 注解。以下代码对 retryAnalyzer 注解进行修改设置

1.2.1 代码

创建类 RetryListener,代码如下。


public class RetryListener implements IAnnotationTransformer {
public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
IRetryAnalyzer retry = annotation.getRetryAnalyzer(); if (retry == null) { annotation.setRetryAnalyzer(RetryImpl.class); } }}
复制代码

1.2.2 配置 Listener

TestNG 可以在配置文件或者测试类中对Listener类进行配置。


  • 方法一:在 TestNG 的配置 XML 中进行以下配置

  • 方法二:在测试类中通过 @Listeners 配置


@Listeners({RetryListener.class})public class TestNGReRunDemo {    @Test    public void test01(){        Assert.assertEquals("success","fail");        System.out.println("test01");    }}
复制代码


配置完成后,运行测试用例 test01,运行结果显示 test01 将重跑次数 3 次。

二、被依赖的测试用例重跑结果处理

进一步分析 TestNG 的运行代码,其在对失败运行用例重跑时,逻辑如下图。



对于通过 dependsOnMethods 或 dependsOnGroups 注解依赖于其他测试用例的测试用例来讲 ,测试用例执行分为两种情况:


  • alwaysRun=true: 则无论所依赖的测试用例执行情况如何,该测试用例都会执行, 即所依赖的测试用例重跑不会影响该测试用例的执行

  • alwaysRun=false: 或者保持缺省值(false),依赖于其他测试用例或测试用例组的测试结果,在运行时 TestNG 获取所依赖的测试用例的运行结果,检查依赖的测试用例是否全部执行成功,如果不全部成功,则 把该测试用例结果设置为 Skipped

2.1 场景分析:场景一

被依赖的测试用例失败后进行了重跑,并重跑成功。(注:在 RetryImpl 类中,已设置最大重跑次数 max_count = 3


public static int number =0;
@Testpublic void test01(){number++;System.out.println(String.valueOf(number));Assert.assertEquals(number,2); System.out.println("test01");}
@Test(dependsOnMethods = "test01") // alwaysRun = false by defaultpublic void test02(){ System.out.println("test02 is running only if test01 is passed.");}
复制代码


1、TestNG 测试报告



2、问题


测试用例运行次数运行情况测试报告
Test012第一次:skipped ; 第二次:passed在Skipped 和Passed的统计数量中,test01被分别记录一次
Test020Skipped记录一次Skipped


  • 测试报告: test01 运行结果全部被记录,而用例重跑,只希望记录最后的结果。

  • 运行情况: 测试用例 test02 依赖于测试用例 test01 运行结果,在 test01 重跑成功后,测试用例 test02 没有执行,不符合需求预期

2.2 场景分析:场景二

被依赖的测试用例失败后进行了重跑,并且重跑没有成功。(注:在 RetryImpl 类中,已设置最大重跑次数 max_count = 3)


public static int number =0;@Testpublic void test01(){number++;System.out.println(String.valueOf(number));Assert.assertEquals(number,10);    System.out.println("test01");}
@Test(dependsOnMethods = "test01") // alwaysRun = false by defaultpublic void test02(){ System.out.println("test02 is running only if test01 is passed.");}
复制代码


1、TestNG 测试报告



2、问题


测试用例运行次数运行结果测试报告
Test013第一次:skipped;第二次:skipped;第三次:failed在Skipped统计数量中,test01被被记录两次在failed统计中,test01被记录一次
Test020Skipped记录一次Skipped


  • 运行情况: 测试用例 test02 依赖于测试用例 test01 运行结果, 在 test01 重跑失败后,测试用例 test02 没有执行,这种情况符合需求预期

  • 测试报告: 同场景一,test01 重跑失败,运行结果全部被记录,而用例重跑,只希望记录最后的结果。

三、优化解决方案

以下方案解决重跑测试用例成功后后继测试用例无法继续运行的问题,并对测试报告进行优化。

3.1 TestListenerAdapter 方法重写

根据上面分析的 TestNG 逻辑,在对依赖测试用例的结果进行检查如果忽略重跑的中间结果只检查最后一次的运行结果,可以达到需求的。对于测试报告,同样的处理方式,忽略所有中间的测试用例运行结果,只记录最后结测试用例的中间运行结果为 Skipped,下面的代码通过重写 TestListenerAdapter 的 onTestSuccess()和 onTestFailure()方法,对测试用例的中间结果 skipped 进行了。代码如下:


public class ResultListener extends TestListenerAdapter {    @Override    public void onTestFailure(ITestResult tr) {        if(tr.getMethod().getCurrentInvocationCount()==1)        {            super.onTestFailure(tr);            return;        }
processSkipResult(tr); super.onTestFailure(tr); } @Override public void onTestSuccess(ITestResult tr) { if(tr.getMethod().getCurrentInvocationCount()==1) { super.onTestSuccess(tr); return; } processSkipResult(tr); super.onTestSuccess(tr); } // Remove all the dup Skipped results public void processSkipResult(ITestResult tr) { ITestContext iTestContext = tr.getTestContext(); Iterator<ITestResult> processResults = iTestContext.getSkippedTests().getAllResults().iterator(); while (processResults.hasNext()) { ITestResult skippedTest = (ITestResult) processResults.next(); if (skippedTest.getMethod().getMethodName().equalsIgnoreCase(tr.getMethod().getMethodName()) ) { processResults.remove(); } } }}
复制代码

3.2 配置结果处理 Listener 类

在配置文件进行全局设置或者在测试类中标记。


  • 方法一:在 TestNG 的配置 XML 中进行以下配置

  • 方法二:在测试类中通过 @Listeners 配置

3.3 场景一

1、 结果验证



2、结果分析


测试用例运行次数运行结果测试报告
Test012第一次:skipped;第二次:passed只在Passed的统计数量中test01被记录一次
Test021Passed记录一次passed

3.4 场景二

1、结果验证



2、结果分析


测试用例运行次数运行结果测试报告
Test013第一次:skipped;第二次:skipped;第三次:failedtest01只在failed统计中被记录一次
Test021Skipped依赖用例执行失败,test02结果为Skipped,只记录一次结果Skipped


本文转载自公众号宜信技术学院(ID:CE_TECH)。


原文链接


https://mp.weixin.qq.com/s/PLF-1RXbMPU1mWpOwiSamA


2020-05-26 10:001925

评论

发布
暂无评论
发现更多内容

架构师训练营 - 第七周 - 作业一

行者

Android网络性能监控方案

移动研发平台EMAS

android 性能 监控 移动开发 应用

完美!阿里P8都赞不绝口的世界独一份489页SQL优化笔记

Java~~~

Java 数据库 程序员 架构师 SQL优化

数字货币OTC交易所开发,交易所搭建方案

13530558032

从智慧计算的点、线、面,读懂浪潮AI的进化轨迹

脑极体

算法有多重要,看字节腾讯等公司面试多重视就行了

小Q

Java 学习 架构 面试 算法

一不小心画了 24 张图剖析计网应用层协议!

苹果看辽宁体育

计算机网络 计算机 协议

京东技术中台Flutter实践之路(二)

京东科技开发者

开源 中台 大前端 Web UI

USDT承兑支付平台技术开发,承兑商币支付交易平台搭建

13530558032

从一场“众盟科技云滇之播”,我们发现了美食直播的商业与公益价值

脑极体

18张图,揭开阿里巴巴开发手册强制使用SLF4J作为门面担当的秘密

沉默王二

Java slf4j 日志系统

音像协呼吁保护音乐版权:短视频平台成为侵权重灾区

石头IT视角

做个别人家的网页

MySQL从删库到跑路

html/css 网页设计

《TED TALKS演讲的力量》阅读笔记

Jesse Xing

读书笔记 演讲 TED

不会这些mysql得面试题,那可能说明你要回炉了

小Q

Java MySQL 数据库 学习 面试

JMeter100个线程竟然只模拟出1个并发

dongfanger

软件测试 Jmeter 性能测试 压力测试 测试工具

初级工程师职场生存要点

javaadu

程序员 职场成长 开发日志

神经网络之dropout总述

Dreamer

学习

Java垃圾回收GC概览

Java JVM GC

金融科技的未来

CECBC

金融

一款区块链钱包开发需要多少钱?数字资产钱包开发搭建

13530558032

害怕重构?都怪我太晚和你介绍该如何重构,现在我来了

小Q

Java 学习 程序员 面试 重构

《乌合之众——群体心理研究》读书笔记

Jesse Xing

读书笔记 心理学 乌合之众 群体心理学

接口测试的时候如何生成随机数据进行测试

测试人生路

接口测试

《精进:如何成为一个很厉害的人》阅读心得

Jesse Xing

读书笔记 个人成长 精进

搭建一套ASP.NET Core+Nacos+Spring Cloud Gateway项目

yi念之间

魏际刚:精准谋划我国供应链发展新方位

CECBC

供应链 物流

CDN是什么?

德胜网络-阳

面试前不陪女朋友也要看完这套spring源码面试题(附答案)

小Q

Java 学习 编程 架构 面试

从接口异常说说线上问题排查流程

QiLab

线上排障 指标监控 链路监控 日志监控 排障流程

jdk 源码系列之ReentrantLock

sinsy

源码 jdk ReentrantLock 公平锁 非公平锁

TestNG测试用例重跑详解及实践优化_软件工程_耿燕飞_InfoQ精选文章