此文已由作者范旭斐授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

抛砖引玉

假设我们将testng作为自动化测试框架的选型方案,以下两个问题如何实现:

问题1:如何将每次执行(手动或CI自动构建)后的每一条TestNG的测试结果,包括用例的描述,分组,优先级,执行日志,执行结果等存储到数据库存档?

问题2:在UI自动化测试中,如何实现用例失败自动截屏功能?

TestNG的Listener列表

TestNG提供了一组预定义的Listener Java接口,这些接口全部继承自TestNG的 ITestNGListener接口。用户创建这些接口的实现类,并把它们加入到 TestNG 中,TestNG便会在测试运行的不同时刻调用这些类中的接口方法:

  • IExecutionListener 监听TestNG运行的启动和停止。
  • IAnnotationTransformer 注解转换器,用于TestNG测试类中的注解。
  • ISuiteListener 测试套件监听器,监听测试套件的启动和停止。
  • ITestListener 测试运行的监听器。
  • IConfigurationListener 监听配置方法相关的接口。
  • IMethodInterceptor 用于修改TestNG即将运行的测试方法列表。
  • IInvokedMethodListener 测试方法拦截监听,用于获取被TestNG调用的在Method的Before 和After方法监听器。该方法只会被配置和测试方法调用。
  • IHookable 若测试类实现了该接口,当@Test方法被发现时,它的run()方法将会被调用来替代@Test方法。这个测试方法通常在IHookCallBack的callback之上调用,比较适用于需要JASS授权的测试类。
  • IReporter 实现该接口可以生成一份测试报告。

本文将着重介绍最常用到的两个Listener ITestListener与Ireporter接口。ITestListener

实现ITestListener接口的类在加入TestNG后,会在用例执行期间,测试类加载后,每个测试方法@Test之前前后调用执行。以下为实现了该接口的一个demo类:

import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import org.testng.Reporter;public class KaolaTestListener implements ITestListener {//用例执行结束后,用例执行成功时调用public void onTestSuccess(ITestResult tr) {logTestEnd(tr, "Success");}//用例执行结束后,用例执行失败时调用public void onTestFailure(ITestResult tr) {logTestEnd(tr, "Failed");}//用例执行结束后,用例执行skip时调用public void onTestSkipped(ITestResult tr) {logTestEnd(tr, "Skipped");}//每次方法失败但是已经使用successPercentage进行注释时调用,并且此失败仍保留在请求的成功百分比之内。public void onTestFailedButWithinSuccessPercentage(ITestResult tr) {logTestEnd(tr, "FailedButWithinSuccessPercentage");}//每次调用测试@Test之前调用public void onTestStart(ITestResult result) {logTestStart(result);}//在测试类被实例化之后调用,并在调用任何配置方法之前调用。public void onStart(ITestContext context) {return;}//在所有测试运行之后调用,并且所有的配置方法都被调用public void onFinish(ITestContext context) {return;}// 在用例执行结束时,打印用例的执行结果信息protected void logTestEnd(ITestResult tr, String result) {Reporter.log(String.format("=============Result: %s=============", result), true);}// 在用例开始时,打印用例的一些信息,比如@Test对应的方法名,用例的描述等等protected void logTestStart(ITestResult tr) {Reporter.log(String.format("=============Run: %s===============", tr.getMethod()), true);Reporter.log(String.format("用例描述: %s, 优先级: %s", tr.getMethod().getDescription(), tr.getMethod().getPriority()),true);return;}
}

这里写一个简单的demo用例:

@Test(description = "demo用例的示例描述", priority = 0)public void demo() throws IOException, InterruptedException {Reporter.log("步骤1:调用接口", true);HttpGetAPI getAPI = new HttpGetAPI();getAPI.setHost("127.0.0.1");getAPI.setPort("9999");getAPI.setPath("/api/sayHello");getAPI.getUriParams().put("name", "luck");getAPI.sendRequest(null);Reporter.log("步骤2:接口调用结果校验", true);getAPI.verifyResponseStatus(200, "");}

将示例的 KaolaTestListener类加入到 TestNG 中,运行一个demo用例后的执行效果如下:

可以看到用例运行后的日志中,在用例开始前与结束后添加了我们写的借助 org.testng.Reporter打印的一些日志信息。

IReporter

实现IReporter接口加入TestNG后,在每次测试执行完后被调用执行,可以获取所有执行的test suite,testcase的一些信息,可以用以生成一份测试报告。以下基于 extentreports实现IReporter接口,自定义扩展的测试报告为例:

extentreports是 aventstack公司开发的一个报告工具,其提供了开源版本,支持java及.net项目的测试报告生成。extentreports的一些介绍:http://extentreports.com/docs/versions/3/java/

extentreports的pom依赖:

<dependency><groupId>com.aventstack</groupId><artifactId>extentreports</artifactId><version>3.1.5</version></dependency><!-- https://mvnrepository.com/artifact/aopalliance/aopalliance --><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><!-- https://mvnrepository.com/artifact/aspectj/aspectjweaver --><dependency><groupId>aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.5.4</version></dependency>

基于extentreports实现的IReporter接口:

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;import org.testng.IReporter;
import org.testng.IResultMap;
import org.testng.ISuite;
import org.testng.ISuiteResult;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.xml.XmlSuite;import com.netease.kaola.onlinetest.test.common.ExtentReportsContext;
import com.alibaba.fastjson.JSONObject;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.Status;
import org.apache.commons.lang3.StringUtils;public class ExtentTestNGReporter implements IReporter {List groups = new ArrayList();public void generateReport(List xmlSuites, List suites, String outputDirectory) {for (ISuite suite : suites) {Map result = suite.getResults();for (ISuiteResult r : result.values()) {ITestContext context = r.getTestContext();ExtentTest parent = ExtentReportsContext.getInstance().createTest(context.getSuite().getName());ExtentReportsContext.parentTest.set(parent);buildTestNodes(context.getFailedTests(), Status.FAIL);buildTestNodes(context.getSkippedTests(), Status.SKIP);buildTestNodes(context.getPassedTests(), Status.PASS);for (String group : groups) {ExtentReportsContext.parentTest.get().assignCategory(group);ExtentReportsContext.getInstance().flush();}groups.clear();}}for (String s : org.testng.Reporter.getOutput()) {ExtentReportsContext.getInstance().setTestRunnerOutput(s);}}private void buildTestNodes(IResultMap tests, Status status) {if (tests.size() > 0) {for (ITestResult result : tests.getAllResults()) {ExtentTest child = ExtentReportsContext.parentTest.get().createNode(result.getMethod().getMethodName());ExtentReportsContext.test.set(child);String groupsStr = "";for (String group : result.getMethod().getGroups()) {if (!groups.contains(group)) {groups.add(group);}if (!StringUtils.isEmpty(groupsStr)) {groupsStr += "|";}groupsStr += group;ExtentReportsContext.test.get().assignCategory(group);}if (!StringUtils.isEmpty(result.getMethod().getDescription())) {ExtentReportsContext.test.get().log(Status.PASS,String.format("用例描述:%s Priority:%s 分组:%s", result.getMethod().getDescription(),Integer.toString(result.getMethod().getPriority()), groupsStr));}if (result.getParameters().length > 0) {for (int i = 0; i < result.getParameters().length; i++) {ExtentReportsContext.test.get().log(Status.PASS, "用例参数列表:");ExtentReportsContext.test.get().log(Status.PASS, String.format("第%d个参数:", i + 1));ExtentReportsContext.test.get().log(Status.PASS,JSONObject.toJSONString(result.getParameters()[i]));}}ExtentReportsContext.test.get().log(Status.PASS,String.format("=============Run: %s===============", result.getMethod()));List outputs = Reporter.getOutput(result);if (outputs != null) {for (String output : outputs) {ExtentReportsContext.test.get().log(Status.PASS, output);}}if (result.getThrowable() != null) {ExtentReportsContext.test.get().log(status, result.getThrowable());}ExtentReportsContext.test.get().getModel().setStartTime(getTime(result.getStartMillis()));ExtentReportsContext.test.get().getModel().setEndTime(getTime(result.getEndMillis()));}ExtentReportsContext.getInstance().flush();}}private Date getTime(long millis) {Calendar calendar = Calendar.getInstance();calendar.setTimeInMillis(millis);return calendar.getTime();}
}

import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.ResourceCDN;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.aventstack.extentreports.reporter.configuration.ChartLocation;public class ExtentReportsContext {private static ExtentReports extent;public static ThreadLocal parentTest = new ThreadLocal();public static ThreadLocal test = new ThreadLocal();public static ExtentReports getInstance() {if (extent == null)createInstance("接口测试报告.html");return extent;}public static ExtentReports createInstance(String fileName) {ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(fileName);htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);htmlReporter.config().setChartVisibilityOnOpen(true);// htmlReporter.config().setTheme(Theme.STANDARD);htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);htmlReporter.config().setDocumentTitle(fileName);htmlReporter.config().setEncoding("utf-8");htmlReporter.config().setReportName(fileName);extent = new ExtentReports();extent.attachReporter(htmlReporter);return extent;}
}

这里 ExtentReportsContext与 ExtentTestNGReporter的编写主要参考extentreports的官方文档:http://extentreports.com/docs/versions/3/java/#testng-ireporter

将示例的ExtentTestNGReporter类加入到 TestNG 中,运行一个demo用例后,可以在target目录下查看生成的接口测试报告 “接口测试报告.html”:

可以看到extentreports比原生的testng生成的测试报告界面更友好,内容也更丰富。因为我们在ExtentTestNGReporter用到了org.testng.Reporter. getOutput(ITestResult tr )方法,所以所有调用到org.testng.Reporter. log()方法的地方,都可以在测试报告中展示出来。

如何将实现了ITestNGListener接口加入到TestNG中

这里有三个地方可以为TestNG配置Listener

  • pom文件中maven-surefire-plugin插件,maven-surefire-plugin插件集成了TestNG&JUnit,任何实现了ITestNGListener的接口的类都可以配置进来。
   <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>${version.maven.plugins}</version><configuration><useSystemClassLoader>true</useSystemClassLoader><testFailureIgnore>true</testFailureIgnore><parallel>false</parallel><forkMode>once</forkMode><suiteXmlFiles><suiteXmlFile>src/main/resources/testng/${run}-${runtype}-testng.xml</suiteXmlFile></suiteXmlFiles><properties><property><name>usedefaultlisteners</name><value>false</value></property><property><name>listener</name><value>org.uncommons.reportng.HTMLReporter,org.uncommons.reportng.JUnitXMLReporter,com.netease.kaola.onlinetest.test.common.ExtentTestNGReporter</value></property></properties><workingDirectory>target/</workingDirectory></configuration></plugin>

testng的xml配置文件中

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="dubbok_bvt_testsuite" verbose="1" parallel="false"><test name="dubbok_bvt_testsuite"><groups><run><include name="dubbok" /></run></groups><packages><package name="com.netease.kaola.onlinetest.test.bvt.dubbok.*" /></packages></test><listeners><listener class-name="com.netease.kaola.onlinetest.test.common.ExtentTestNGReporter" /><listener class-name="com.netease.kaola.onlinetest.test.common.KaolaTestListener" /></listeners>
</suite>

测试类添加注解标签

package com.netease.kaola.onlinetest.test.base;import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Listeners;import com.netease.kaola.onlinetest.test.common.KaolaTestListener;@Listeners({ KaolaTestListener.class })
@ContextConfiguration(locations = { "classpath:application.xml" })
public abstract class BaseTest extends AbstractTestNGSpringContextTests {
}

回到开始的问题

假设我们将testng作为自动化测试框架的选型方案,以下两个问题如何实现:

问题1:如何将每次执行(手动或CI自动构建)后的每一条TestNG的测试结果,包括用例的描述,分组,优先级,执行日志,执行结果等存储到数据库存档?

答案:实现一个TestNG的IReporter接口,在自定义的Reporter类中,将需要的一些用例信息收集出来,保存在数据库中,最后将自定义的Reporter类配置在TestNG中。

问题2:在UI自动化测试中,如何实现用例失败自动截屏功能?

答案:实现一个TestNG的 ITestListener 接口,在自定义的TestListener类的 onTestFailure()方法中实现截屏逻辑 ,最后将自定义的 TestListener 类配置在TestNG中。

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击。

freemarker的测试结果框架_TestNG框架Listener介绍及测试结果的收集相关推荐

  1. Kail Linux渗透测试教程之Recon-NG框架

    Kail Linux渗透测试教程之Recon-NG框架 信息收集 信息收集是网络攻击最重要的阶段之一.要想进行渗透攻击,就需要收集目标的各类信息.收集到的信息越多,攻击成功的概率也就越大.本章将介绍信 ...

  2. spring框架mvc框架_Spring的MVC测试框架入门–第1部分

    spring框架mvc框架 最新推出的主要Spring框架是Spring MVC测试框架,Spring Guys声称它是"一流的JUnit支持,可通过流畅的API测试客户端和服务器端Spri ...

  3. spring框架mvc框架_Spring MVC测试框架入门–第2部分

    spring框架mvc框架 这个迷你系列的第一个博客介绍了Spring MVC测试框架,并演示了其在单元测试Spring MVC Controller类中作为控制器而不是POJO进行单元测试的用途. ...

  4. QTP自动化测试从零基础到精通进阶(脚本测试、VBS语法、描述性编程、测试框架)...

    QTP自动化测试从零基础到精通进阶(脚本测试.VBS语法.描述性编程.测试框架) 网盘地址:https://pan.baidu.com/s/19yUptC8PBFZFfH-VpyUUpQ 提取码: a ...

  5. 2020年你不可不知的自动化框架,可替代Selenuim的测试框架Top10

    Selenium是一种开源自动测试工具.它可以跨不同的浏览器和平台在Web应用程序上执行功能,回归,负载测试.Slenium是最好的工具之一,但确实有一些缺点. 业界有一些强大的工具可以替代Selen ...

  6. 什么是数据驱动测试?学习创建框架

    数据驱动测试 数据驱动测试是一种软件测试方法,其中测试数据以表或电子表格格式存储.数据驱动的测试允许测试人员输入单个测试脚本,该脚本可以对表中的所有测试数据执行测试,并期望测试输出在同一表中.也称为表 ...

  7. 自动测试如何选择自动化测试框架_机器擅长回归测试,人类善于寻找Bug _Pekka Klärck

    Robot Framework作者建议自动测试如何选择自动化测试框架.软件自动化测试,作为手工测试的替代,越来越受到关注.Pekka Klrck,作为Robot Framework的创建者和核心开发者 ...

  8. android开发自制计算器测试图,基于uiautomator测试框架的计算器自动化测试方法和测试系统的制作方法...

    基于uiautomator测试框架的计算器自动化测试方法和测试系统的制作方法 [技术领域] [0001]本发明涉及自动化测试技术领域,特别是一种基于uiautomator测试框架的计算器自动化测试方法 ...

  9. Unittest自动化测试框架教程(五)——Python中的测试套件TestSuite

      "  本文介绍了unittest中测试套件的相关概念,并通过实例帮助理解测试套件存在的意义,自由享用即可." PS:" 老规矩,老手or实战应用型用户文末看总结,学习 ...

最新文章

  1. oracle视图能增删改,oracle视图的增删改
  2. china-pub赠书啦,超值畅销书5本
  3. map的用法-HD 1029Ignatius and the Princess IV
  4. 导入Oracle 数据库镜像,创建Oracle虚拟机_01
  5. scrapy使用meta在各个模块的组件之间传递数据
  6. Windows下安装NPM
  7. AcWing 1978. 奶牛过马路(前缀和)
  8. Hexo报错Usage: hexo command处理及图片显示问题
  9. dfs.datanode.directoryscan.throttle.limit.ms.per.
  10. 考勤系统之计算工作小时数
  11. 易如意php,易如意网络验证系统1.1【开源】
  12. Android TelephonyManager获取LET信息及手机基本信息
  13. bootstrap基础表单样式
  14. 1585 Amount of Degrees
  15. mysql会话是什么意思_MySQL会话临时表空间有什么作用
  16. 飞鱼星路由器如何限制外网访问服务器网站,飞鱼星路由器怎么访问指定网站
  17. 洛谷P1330 封锁阳光大学(BFS, 并查集)
  18. 计算机网络_实验5_集线器与交换机对比
  19. python人工智能思想_从零开始学人工智能(1)--Python · 神经网络(零)
  20. 刀口舔血,步步惊心!——Android中小开发者/团队广告盈利全攻略

热门文章

  1. python如何进行双色球预测最准确_【原创】python基于大数据现实双色球预测
  2. c语言6字符宽度和小数位数,2017年计算机二级C语言考点复习
  3. 最新增值税商品税目编码表_大家好!我叫增值税!这是我的最新最全税率表
  4. 2017年10月21日普及组 简单单词
  5. UOJ #277 BZOJ 4739 定向越野 (计算几何、最短路)
  6. 前端中全部盒子靠左对齐_前端面试一百问之弹性盒子中 flex: 0 1 auto 表示什么意思...
  7. python web开发环境_Flask_Web 开发环境搭建
  8. python字符串逆序_python之字符串逆序
  9. 20211201 (正定矩阵A+正定矩阵B)的最小特征值 ≥ 正定矩阵A的最小特征值+正定矩阵B的最小特征值
  10. 中间件方法必须返回Response对象实例(tp5.1+小程序结合时候出的问题)