概述
上一章:
一、TestNG的基本使用_傲娇的喵酱的博客-CSDN博客
目录
一、背景:
二、各种监听器的介绍
2.1 IAnnotationTransformer 只能用来修改框架提供的 @Test 注解
2.2 IAnnotationTransformer2 修改除 @Test 以外的 TestNG 的注解
2.3 IHookable:TestNG在测试方法执行前执行,常用于授权检查。
2.4 IInvokedMethodListener常用于日志的采集。
2.5 IReporter
2.6 ISuiteListener
2.7 ITestListener
三、代码实现
四、具体应用
一、背景:
我想要的不仅仅是测试报告,我想单独把测试case运行结果的数据拿出来,分析判断做各种处理。
比如:
1、谁写的case执行失败了,会将相关失败case信息推送到对应作者
2、报警策略,P0级别的case执行失败需要报警
等等等。
这就需要我们拿到case执行的结果信息。
二、各种监听器的介绍
参考链接:
TestNG测试框架之监听器详解_慕城南风的博客-CSDN博客_testng监听器
我们通过监听器实现,上一章,生成的测试报告就是通过监听器实现的。
监听器:
监听器实际上是一些预定义的java接口,用户创建这些接口的实现类(即implements某监听接口,并实现里面的方法),并加入到testng中,testng便会在运行的不同时刻调用这些类中你自定义实现的接口方法,从而实现定制额外的功能;
2.1 IAnnotationTransformer 只能用来修改框架提供的 @Test 注解
当框架提供的@Test注解,不能满足我们需求时,我们可以通过IAnnotationTransformer来修改@Test注解。
IAnnotationTransformer 要求实现 transform 方法,其方法签名如下:
void transform(ITest annotation, Class testClass, Constructor testConstructor, Method testMethod);
annotation 代表的就是为 testMethod 定义的 @Test 注解。调用其方法可以更改 @Test 注解属性。例如,下面的代码在运行时将属性 enabled 改为 false 从而禁用了当前的测试方法。
annotation.setEnabled(false);
2.2 IAnnotationTransformer2 修改除 @Test 以外的 TestNG 的注解
2.3 IHookable:TestNG在测试方法执行前执行,常用于授权检查。
IHookable接口继承自ITestNGListener接口,其定义了唯一的run方法。IHookable 监听器提供了类似与面向切面编程(AOP)中的 Around Advice 的功能。它在测试方法执行前后提供了切入点,从而使用户能够在测试方法运行前后注入特定的功能。例如,用户可以在当前测试方法运行前加入特定的验证逻辑以决定测试方法是否运行或者跳过,甚至覆盖测试方法的逻辑。下面是 IHookable 监听器要求实现的方法签名。
public interface IHookable extends ITestNGListener {
void run(IHookCallBack var1, ITestResult var2);
}
如要运行原始测试方法逻辑,需要调用 runTestMethod 方法。
callBack.runTestMethod(testResult);
2.4 IInvokedMethodListener常用于日志的采集。
与 IHookable 类似,IInvokedMethodListener 提供了类似与面向切面编程(AOP)中的 Before Advice 和 After Advice 的功能。IInvokedMethodListener接口继承自ITestNGListener接口,其定义了beforeInvocation和afterInvocation方法。TestNG在调用方法前、后启用该监听器,常用于日志的采集。
public interface IInvokedMethodListener extends ITestNGListener {
void beforeInvocation(IInvokedMethod var1, ITestResult var2);
void afterInvocation(IInvokedMethod var1, ITestResult var2);
}
2.5 IReporter
TestNG 提供了默认的测试报表。但如果用户希望有不同格式的测试报表,就需要使用 IReporter 监听器。IReporter 监听器只有一个方法需要实现。
public interface IReporter extends ITestNGListener {
void generateReport(List<XmlSuite> var1, List<ISuite> var2, String outputDirectory);
}
IReporter接口继承自ITestNGListener接口,其定义了generateReport方法。TestNG在运行所有套件时都将调用此方法,通过遍历 xmlSuites 和 suites 能够获取所有测试方法的信息以及测试结果,后续可用于自定义测试报告。outputDirectory 是默认的测试报表生成路径,当然你可以指定其他路径生成报表。
2.6 ISuiteListener
ISuiteListener接口继承自ITestNGListener接口。ISuiteListener 类似于 IInvokedMethodListener,区别是 IInvokedMethodListener 针对的是测试方法,而 ISuiteListener 针对的是测试套件。ISuiteListener 使用户有机会在测试套件开始执行以及执行结束之后嵌入自己的逻辑。该监听器要求实现的方法如下。
public interface ISuiteListener extends ITestNGListener {
void onStart(ISuite var1);
void onFinish(ISuite var1);
}
2.7 ITestListener
如果要在测试方法执行成功、失败或者跳过时指定不同后续行为,可以通过 IInvokedMethodListener 实现,不过更为简便的方式是利用 ITestListener 监听器。ITestListener 接口继承自ITestNGListener接口,ITestListener 监听器要求实现的方法中包含如下三个。
public interface ITestListener extends ITestNGListener {
//每次调用测试之前都会调用
void onTestStart(ITestResult var1);
//每次测试成功时调用。
void onTestSuccess(ITestResult var1);
//每次测试失败时调用。
void onTestFailure(ITestResult var1);
//每次测试跳过时调用。
void onTestSkipped(ITestResult var1);
//执行测试失败且 successPercentage属性满足条件是调用
void onTestFailedButWithinSuccessPercentage(ITestResult var1);
//在实例化测试类之后和在调用任何配置方法之前调用。
void onStart(ITestContext var1);
//在运行所有测试并调用所有配置方法之后调用。
void onFinish(ITestContext var1);
}
除了以上三个方法,ITestListener 还声明了其他一些方法,大家可以自行查阅 TestNG Javadoc 了解细节。
另外,TestListenerAdapter 已经实现 ITestListener,并且提供了一些有用的方法,比如分别获取所有成功失败跳过三种测试结果的测试方法的方法,并且 ITestListner 中有很多方法而 TestListenerAdapter 已给出了默认实现。因此,在实际应用过程中我们只需继承 TestListenerAdapter 后,只关注需要修改的方法。
三、代码实现
我们收集case运行结果的信息,然后发送出去。只要实现IReporter这个接口就可以了。
package my_listener;
import org.testng.*;
import org.testng.xml.XmlSuite;
import java.text.SimpleDateFormat;
import java.util.*;
public class MySendTestReport implements IReporter {
private int testsPass = 0;
private int testsFail = 0;
private int testsSkip = 0;
private String beginTime;
private long totalTime;
private String name;
public MySendTestReport() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmssSSS");
name = formatter.format(System.currentTimeMillis());
}
public MySendTestReport(String name) {
this.name = name;
if (this.name == null) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmssSSS");
this.name = formatter.format(System.currentTimeMillis());
}
}
@Override
public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
//通过遍历 xmlSuites 和 suites 能够获取所有测试方法的信息以及测试结果,后续可用于自定义测试报告。
List<ITestResult> list = new ArrayList<ITestResult>();
// 遍历List<ISuite> suites集合
for (ISuite suite : suites) {
Map<String, ISuiteResult> suiteResults = suite.getResults();
for (ISuiteResult suiteResult : suiteResults.values()) {
ITestContext testContext = suiteResult.getTestContext();
IResultMap passedTests = testContext.getPassedTests();
testsPass = testsPass + passedTests.size();
IResultMap failedTests = testContext.getFailedTests();
testsFail = testsFail + failedTests.size();
IResultMap skippedTests = testContext.getSkippedTests();
testsSkip = testsSkip + skippedTests.size();
IResultMap failedConfig = testContext.getFailedConfigurations();
list.addAll(this.listTestResult(passedTests));
list.addAll(this.listTestResult(failedTests));
list.addAll(this.listTestResult(skippedTests));
list.addAll(this.listTestResult(failedConfig));
}
}
this.sort(list);
this.outputResult(list);
}
private ArrayList<ITestResult> listTestResult(IResultMap resultMap) {
Set<ITestResult> results = resultMap.getAllResults();
return new ArrayList<ITestResult>(results);
}
private void sort(List<ITestResult> list) {
Collections.sort(list, new Comparator<ITestResult>() {
@Override
public int compare(ITestResult r1, ITestResult r2) {
if (r1.getStartMillis() > r2.getStartMillis()) {
return 1;
} else {
return -1;
}
}
});
}
private void outputResult(List<ITestResult> list) {
try {
List<ReportInfo> listInfo = new ArrayList<ReportInfo>();
int index = 0;
for (ITestResult result : list) {
String tn = result.getTestContext().getCurrentXmlTest().getParameter("testCase");
if (index == 0) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
beginTime = formatter.format(new Date(result.getStartMillis()));
index++;
}
long spendTime = result.getEndMillis() - result.getStartMillis();
totalTime += spendTime;
String status = this.getStatus(result.getStatus());
List<String> log = Reporter.getOutput(result);
for (int i = 0; i < log.size(); i++) {
log.set(i, this.toHtml(log.get(i)));
}
Throwable throwable = result.getThrowable();
if (throwable != null) {
log.add(this.toHtml(throwable.toString()));
StackTraceElement[] st = throwable.getStackTrace();
for (StackTraceElement stackTraceElement : st) {
log.add(this.toHtml(" " + stackTraceElement));
}
}
ReportInfo info = new ReportInfo();
info.setName(tn);
info.setSpendTime(spendTime + "ms");
info.setStatus(status);
info.setClassName(result.getInstanceName());
info.setMethodName(result.getName());
info.setDescription(result.getMethod().getDescription());
info.setLog(log);
listInfo.add(info);
if(status == "失败"){
System.out.println("case名称:"+result.getName());
System.out.println("测试类:"+result.getInstanceName());
System.out.println("耗时:"+spendTime + "ms");
System.out.println("用例描述:"+result.getMethod().getDescription());
System.out.println("失败原因"+log);
}
}
Map<String, Object> result = new HashMap<String, Object>();
result.put("testPass", testsPass);
result.put("testFail", testsFail);
result.put("testSkip", testsSkip);
result.put("testAll", testsPass + testsFail + testsSkip);
result.put("beginTime", beginTime);
result.put("totalTime", totalTime + "ms");
result.put("testResult", listInfo);
System.out.println("===========================================");
System.out.println("case总数:"+(testsPass + testsFail + testsSkip));
System.out.println("case成功数量:"+testsPass);
System.out.println("case失败数量:"+testsFail);
System.out.println("忽略case数量:"+testsSkip);
System.out.println("开始时间:"+beginTime);
System.out.println("总时间:"+totalTime + "ms");
// System.out.println("result = " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
private String getStatus(int status) {
String statusString = null;
switch (status) {
case 1:
statusString = "成功";
break;
case 2:
statusString = "失败";
break;
case 3:
statusString = "跳过";
break;
default:
break;
}
return statusString;
}
private String toHtml(String str) {
if (str == null) {
return "";
} else {
str = str.replaceAll("<", "<");
str = str.replaceAll(">", ">");
str = str.replaceAll(" ", " ");
str = str.replaceAll("n", "<br>");
str = str.replaceAll(""", "\\"");
}
return str;
}
public static class ReportInfo {
private String name;
private String className;
private String methodName;
private String description;
private String spendTime;
private String status;
private List<String> log;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public String getSpendTime() {
return spendTime;
}
public void setSpendTime(String spendTime) {
this.spendTime = spendTime;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public List<String> getLog() {
return log;
}
public void setLog(List<String> log) {
this.log = log;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
}
现在唯一的缺点就是,收集的数据比较散,需要再次封装一下。
包含:
总的case信息:
case总数:6
case成功数量:4
case失败数量:2
忽略case数量:0
开始时间:2022-09-22 17:43:14.798
总时间:11ms
失败case的信息:
包含失败详情。
---------------------以上收集信息的功能就实现了,剩下的就是把这些信息发送出去-----
具体如何发送信息,就是先封装一下数据,然后发送,参考博客:
八、junit接口自动化框架-钉钉发送报告_傲娇的喵酱的博客-CSDN博客
四、具体应用
我们新建my_test ,然后建2个测试类。
package my_test;
import org.testng.Assert;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import my_listener.MySendTestReport;
@Listeners({MySendTestReport.class})
public class TestDemo1{
@Test(description="case的描述11测试DEMO")
public void testDemo(){
System.out.println("11111111");
Assert.assertEquals("1", "2", "should be equals.");
}
@Test(description="测试DEMO1")
public void testDemo1(){
System.out.println("22222222");
Assert.assertEquals("2", "2", "should be equals.");
}
@Test(description="测试DEMO3")
public void testDemo3(){
System.out.println("333333");
Assert.assertEquals("3", "3", "should be equals.");
}
}
package my_test;
import org.testng.Assert;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import my_listener.MySendTestReport;
@Listeners({MySendTestReport.class})
public class TestDemo2 {
@Test(description="case的描述11测试DEMO")
public void testDemo(){
System.out.println("11111111");
Assert.assertEquals("1", "2", "should be equals.");
}
@Test(description="测试DEMO1")
public void testDemo1(){
System.out.println("22222222");
Assert.assertEquals("2", "2", "should be equals.");
}
@Test(description="测试DEMO3")
public void testDemo3(){
System.out.println("333333");
Assert.assertEquals("3", "3", "should be equals.");
}
}
注意,我们把监听器@Listeners({MySendTestReport.class})直接写到了case上,也可以写到xml文件里。
然后执行xml测试套,就可以正常收集并发送运行结果了。
最后
以上就是高兴秀发为你收集整理的二、TestNG二次开发实现测试结果的报警一、背景:二、各种监听器的介绍三、代码实现四、具体应用的全部内容,希望文章能够帮你解决二、TestNG二次开发实现测试结果的报警一、背景:二、各种监听器的介绍三、代码实现四、具体应用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复