速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

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:001774

评论

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

关于volitile相关知识

北洋

6月月更

Redux之利用 distinct 属性进行性能优化

岛上码农

flutter ios 安卓开发 跨平台应用 6月月更

Spring Data JPA 极速入门

Geek_rze78a

spring data 6月月更

数据结构与算法之时间复杂度与空间复杂度

未见花闻

6月月更

外包学生管理系统架构文档

Asura

OpenKruise v1.2:新增 PersistentPodState 实现有状态 Pod 拓扑固定与 IP 复用

阿里巴巴云原生

阿里云 云原生 开源项目

在线摩斯密码在线翻译转换工具

入门小站

工具

Vue 组件如何在设置 Props

devpoint

Vue Vue3 InfoQ极客传媒15周年庆

学生管理系统详细架构

intelamd

《阅读的方法》:怎么找阅读的乐趣?

郭明

读书笔记

在 Vue3 + Element Plus 中生成动态表格,动态修改表格,多级表头,合并单元格

蒋川

低代码 Vue3 组件 表格 Element Plus

跨境电商如何通过打好数据底座,实现低成本稳步增长

OceanBase 数据库

跨境电商 oceanbase

GameFi如何破圈,AQUANEE靠真正“P2E”展现风采

股市老人

vue指令-4

小恺

6月月更

市场监管总局出手监督618电商促销节:电商促销节存在哪些诟病

石头IT视角

在这些根技术里,读懂华为的“三观”

脑极体

选择数字资产托管人时,要问的 6 个问题

BlockChain先知

RPA能否创造新业态?如何优化组织结构?如何助力疫情中的企业?

王吉伟频道

人工智能 RPA 机器人流程自动化 机器人开发 爱死机

架构设计-外包学生管理系统

Roy

架构实战营

不止于观测|阿里云可观测技术峰会正式上线

阿里巴巴云原生

阿里云 开源 云原生 可观测峰会 行业实践

java面向对象之多态(向上转型和向下转型)

写代码两年半

面向对象 javase 多态 6月月更

linux去掉空行的几种方法

入门小站

Linux

在线JSON转XML工具

入门小站

工具

架构实战营第三模块课后作业

Geek_53787a

课后总结

数据库每日一题---第6天:删除重复的电子邮箱

知心宝贝

数据库 程序员 前端 后端 6月月更

控制并发线程数的Semaphore

急需上岸的小谢

6月月更

架构实战营模块 3 作业

Naoki

架构实战营

3分钟初识网络功能虚拟化NFV

穿过生命散发芬芳

6月月更 NFV

Android App首页主流框架搭建

yechaoa

android kotlin 6月月更 material design

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