testng生成自定义html报告
转自:https://blog.csdn.net/kdslkd/article/details/51198433
testng原生的或reportng的报告总有些不符合需要,尝试生成自定义测试报告,
用到的依赖包:testng-6.9.9.jar,velocity-1.7.jar
1.定义一个DataBean,保存需要收集的数据
只定义部分数据,比如suite、testname、groups等好多数据还没,需要用到的时候再加了
package com.reporter.main; import java.util.Collection;
import java.util.List; import org.testng.ITestNGMethod; public class DataBean {
private int excludeTestsSize; //未执行的test数量
private int passedTestsSize; //测试通过的数量
private int failedTestsSize; //测试失败的数量
private int skippedTestsSize; //测试跳过的数量
private int allTestsSize; //全部执行的测试的数量
private ITestNGMethod[] allTestsMethod; //全部执行的测试方法
private Collection<ITestNGMethod> excludeTestsMethod; //未执行的测试方法
private String testsTime; //测试耗时
private String passPercent; //测试通过率
private String testName; //测试方法名
private String className; //测试类名
private String duration; //单个测试周期
private String params; //测试用参数
private String description; //测试描述
private List<String> output; //Reporter Output
private String dependMethod; //测试依赖方法
private Throwable throwable; //测试异常原因
private StackTraceElement[] stackTrace; // 异常堆栈信息 public int getExcludeTestsSize() {
return excludeTestsSize;
} public void setExcludeTestsSize(int excludeTestsSize) {
this.excludeTestsSize = excludeTestsSize;
} public int getPassedTestsSize() {
return passedTestsSize;
} public void setPassedTestsSize(int passedTestsSize) {
this.passedTestsSize = passedTestsSize;
} public int getFailedTestsSize() {
return failedTestsSize;
} public void setFailedTestsSize(int failedTestsSize) {
this.failedTestsSize = failedTestsSize;
} public int getSkippedTestsSize() {
return skippedTestsSize;
} public void setSkippedTestsSize(int skippedTestsSize) {
this.skippedTestsSize = skippedTestsSize;
} public int getAllTestsSize() {
return allTestsSize;
} public void setAllTestsSize(int allTestsSize) {
this.allTestsSize = allTestsSize;
} public String getPassPercent() {
return passPercent;
} public void setPassPercent(String passPercent) {
this.passPercent = passPercent;
} public String getTestName() {
return testName;
} public void setTestName(String testName) {
this.testName = testName;
} public String getClassName() {
return className;
} public void setClassName(String className) {
this.className = className;
} public String getDuration() {
return duration;
} public void setDuration(String duration) {
this.duration = duration;
} public String getParams() {
return params;
} public void setParams(String params) {
this.params = params;
} public String getDescription() {
return description;
} public void setDescription(String description) {
this.description = description;
} public List<String> getOutput() {
return output;
} public void setOutput(List<String> output) {
this.output = output;
} public String getDependMethod() {
return dependMethod;
} public void setDependMethod(String dependMethod) {
this.dependMethod = dependMethod;
} public Throwable getThrowable() {
return throwable;
} public void setThrowable(Throwable throwable2) {
this.throwable = throwable2;
} public StackTraceElement[] getStackTrace() {
return stackTrace;
} public void setStackTrace(StackTraceElement[] stackTrace) {
this.stackTrace = stackTrace;
} public void setTestsTime(String testsTime) {
this.testsTime = testsTime;
} public String getTestsTime() {
return testsTime;
} public void setAllTestsMethod(ITestNGMethod[] allTestsMethod) {
this.allTestsMethod = allTestsMethod;
} public ITestNGMethod[] getAllTestsMethod() {
return allTestsMethod;
} public void setExcludeTestsMethod(Collection<ITestNGMethod> excludeTestsMethod) {
this.excludeTestsMethod = excludeTestsMethod;
} public Collection<ITestNGMethod> getExcludeTestsMethod() {
return excludeTestsMethod;
} }
2.对需要特别处理的报告元素,比如测试周期、通过率
package com.reporter.main; import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.Reporter; public class ReportUnits {
private static final NumberFormat DURATION_FORMAT = new DecimalFormat("#0.000");
private static final NumberFormat PERCENTAGE_FORMAT = new DecimalFormat("#0.00%");
/**
*测试消耗时长
*return 秒,保留3位小数
*/
public String getTestDuration(ITestContext context){
long duration;
duration=context.getEndDate().getTime()-context.getStartDate().getTime();
return formatDuration(duration);
} public String formatDuration(long elapsed)
{
double seconds = (double) elapsed / 1000;
return DURATION_FORMAT.format(seconds);
}
/**
*测试通过率
*return 2.22%,保留2位小数
*/
public String formatPercentage(int numerator, int denominator)
{
return PERCENTAGE_FORMAT.format(numerator / (double) denominator);
} /**
* 获取方法参数,以逗号分隔
* @param result
* @return
*/
public String getParams(ITestResult result){
Object[] params = result.getParameters();
List<String> list = new ArrayList<String>(params.length);
for (Object o:params){
list.add(renderArgument(o));
}
return commaSeparate(list);
}
/**
* 获取依赖的方法
* @param result
* @return
*/
public String getDependMethods(ITestResult result){
String[] methods=result.getMethod().getMethodsDependedUpon();
return commaSeparate(Arrays.asList(methods));
}
/**
* 堆栈轨迹,暂不确定怎么做,放着先
* @param throwable
* @return
*/
public String getCause(Throwable throwable){
StackTraceElement[] stackTrace=throwable.getStackTrace(); //堆栈轨迹
List<String> list = new ArrayList<String>(stackTrace.length);
for (Object o:stackTrace){
list.add(renderArgument(o));
}
return commaSeparate(list);
}
/**
* 获取全部日志输出信息
* @return
*/
public List<String> getAllOutput(){
return Reporter.getOutput();
} /**
* 按testresult获取日志输出信息
* @param result
* @return
*/
public List<String> getTestOutput(ITestResult result){
return Reporter.getOutput(result);
} /*将object 转换为String*/
private String renderArgument(Object argument)
{
if (argument == null)
{
return "null";
}
else if (argument instanceof String)
{
return "\"" + argument + "\"";
}
else if (argument instanceof Character)
{
return "\'" + argument + "\'";
}
else
{
return argument.toString();
}
}
/*将集合转换为以逗号分隔的字符串*/
private String commaSeparate(Collection<String> strings)
{
StringBuilder buffer = new StringBuilder();
Iterator<String> iterator = strings.iterator();
while (iterator.hasNext())
{
String string = iterator.next();
buffer.append(string);
if (iterator.hasNext())
{
buffer.append(", ");
}
}
return buffer.toString();
}
}
3.测试方法排序,按测试方法执行时间排序
遍历 suites 得到的getAllResults()是一个set 集合,需要对数据进行排序
这里是将getAllResults()转为list,实现Comparable接口的方法进行排序的.
(好像是可以将getAllResults()转成TreeSet排序?)
package com.reporter.main; import org.testng.ITestResult; public class TestResultSort implements Comparable<ITestResult> {
private Long order;
@Override
public int compareTo(ITestResult arg0) {
// TODO Auto-generated method stub
return this.order.compareTo( arg0.getStartMillis());//按test开始时间排序
} }
4.得到测试报告数据
package org.reporter.main; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set; import org.testng.IResultMap;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.Reporter; public class ReporterData {
// 测试结果Set<ITestResult>转为list,再按执行时间排序 ,返回list
public List<ITestResult> sortByTime(Set<ITestResult> str) {
List<ITestResult> list = new ArrayList<ITestResult>();
for (ITestResult r : str) {
list.add(r);
}
Collections.sort(list);
return list; } public DataBean testContext(ITestContext context) {
// 测试结果汇总数据
DataBean data = new DataBean();
ReportUnits units = new ReportUnits();
IResultMap passedTests = context.getPassedTests();
IResultMap failedTests= context.getFailedTests();
IResultMap skipedTests = context.getSkippedTests();
//全部测试周期方法,包括beforetest,beforeclass,beforemethod,aftertest,afterclass,aftermethod
//IResultMap passedConfigurations =context.getPassedConfigurations();
//IResultMap failedConfigurations =context.getFailedConfigurations();
//IResultMap skipedConfigurations =context.getSkippedConfigurations();
Collection<ITestNGMethod> excludeTests = context.getExcludedMethods(); int passedTestsSize = passedTests.size();
int failedTestsSize = failedTests.size();
int skipedTestsSize = skipedTests.size();
int excludeTestsSize = excludeTests.size();
//所有测试结果的数量=测试pass+fail+skip的和,因为数据驱动一个测试方法有多次执行的可能,导致方法总数并不等于测试总数
int allTestsSize= passedTestsSize+failedTestsSize+skipedTestsSize;
data.setAllTestsSize(allTestsSize);
data.setPassedTestsSize(passedTestsSize);
data.setFailedTestsSize(failedTestsSize);
data.setSkippedTestsSize(skipedTestsSize);
data.setExcludeTestsSize(excludeTestsSize);
data.setTestsTime(units.getTestDuration(context));
data.setPassPercent(units.formatPercentage(passedTestsSize, allTestsSize));
data.setAllTestsMethod(context.getAllTestMethods());
data.setExcludeTestsMethod(context.getExcludedMethods()); return data; } public List<DataBean> testResults(IResultMap map, int status) {
// 测试结果详细数据
List<DataBean> list = new ArrayList<DataBean>();
ReportUnits units = new ReportUnits();
map.getAllResults().size();
for (ITestResult result : sortByTime(map.getAllResults())) {
DataBean data = new DataBean();
data.setTestName(result.getName());
data.setClassName(result.getTestClass().getName());
data.setDuration(units.formatDuration(result.getEndMillis()
- result.getStartMillis()));
data.setParams(units.getParams(result));
data.setDescription(result.getMethod().getDescription());
data.setOutput(Reporter.getOutput(result));
data.setDependMethod(units.getDependMethods(result));
data.setThrowable(result.getThrowable());
if (result.getThrowable() != null) {
data.setStackTrace(result.getThrowable().getStackTrace());
}
list.add(data);
}
return list;
} }
5.生成测试报告,生成的测试报告是项目根目录下的report.html(要定制的话再改了)
使用 IReporter 监听器。IReporter 监听器只有一个方法需要实现。
void generateReport(java.util.List<XmlSuite> xmlSuites, java.util.List
<ISuite> suites, java.lang.String outputDirectory)
该方法在所有测试方法执行结束后被调用,通过遍历 xmlSuites 和 suites 能够获取所有测试方法的信息以及测试结果。outputDirectory 是默认的测试报表生成路径,当然你可以指定其他路径生成报表。
package com.reporter.main; import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
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.xml.XmlSuite; public class GenerateReporter implements IReporter {
@Override
public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites,
String outputDirectory) {
// TODO Auto-generated method stub
try {
// 初始化并取得Velocity引擎
VelocityEngine ve = new VelocityEngine();
Properties p = new Properties();
//虽然不懂为什么这样设置,但结果是好的.可以用了
p.setProperty("resource.loader", "class");
p.setProperty("class.resource.loader.class","org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
ve.init(p);
Template t = ve.getTemplate("com/reporter/VMmodel/overview.vm");
VelocityContext context = new VelocityContext(); for (ISuite suite : suites) {
Map<String, ISuiteResult> suiteResults = suite.getResults();
for (ISuiteResult suiteResult : suiteResults.values()) {
ReporterData data = new ReporterData();
ITestContext testContext = suiteResult.getTestContext();
// 把数据填入上下文
context.put("overView", data.testContext(testContext));//测试结果汇总信息
//ITestNGMethod[] allTests = testContext.getAllTestMethods();//所有的测试方法
//Collection<ITestNGMethod> excludeTests = testContext.getExcludedMethods();//未执行的测试方法
IResultMap passedTests = testContext.getPassedTests();//测试通过的测试方法
IResultMap failedTests = testContext.getFailedTests();//测试失败的测试方法
IResultMap skippedTests = testContext.getSkippedTests();//测试跳过的测试方法 context.put("pass", data.testResults(passedTests, ITestResult.SUCCESS));
context.put("fail", data.testResults(failedTests, ITestResult.FAILURE));
context.put("skip", data.testResults(skippedTests, ITestResult.FAILURE)); }
}
// 输出流
<span style="white-space:pre"> </span>//Writer writer = new BufferedWriter(new FileWriter("report.html"));
<span style="white-space:pre"> </span>OutputStream out=new FileOutputStream("report.html");
<span style="white-space:pre"> </span>Writer writer = new BufferedWriter(new OutputStreamWriter(out,"utf-8"));//解决乱码问题
// 转换输出
t.merge(context, writer);
//System.out.println(writer.toString());
writer.flush();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
6.测试报告模板,文件后辍为vm,比如overview.vm,内容差不多就是一个html文件,(要漂亮报告找前端。。。)
<?xml version="1.0" encoding="utf-8" ?> <head>
<title>test</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="description" content="TestNG unit test results." /> </head>
<body> <h1>Test</h1>
<table border="1">
<tr>
<th>OverView........</th>
<th colspan="6" class="header suite">
<div >
<a href="http://www.baidu.com">aaa</a>
</div>
</th>
</tr>
<tr class="columnHeadings">
<td> </td>
<th>all</th>
<th>excluded</th>
<th>passed</th>
<th>faild</th>
<th>skipped</th>
<th>duration(S)</th>
<th>passration</th>
<th>alltestMethod</th>
<th>excluedMethod</th>
</tr> <tr>
<td>TestResult</td>
<td>$overView.allTestsSize</td>
<td>$overView.excludeTestsSize</td>
<td>$overView.passedTestsSize</td>
<td>$overView.failedTestsSize</td>
<td>$overView.skippedTestsSize</td>
<td>$overView.testsTime</td>
<td>$overView.passPercent</td>
<td>
#foreach($p in $overView.allTestsMethod)
$p<br/>
#end
</td>
<td>
#foreach($e in $overView.excludeTestsMethod)
$e<br/>
#end
</td>
</tr>
</table>
<br/><br/>
<table border="1">
<tr>
<th>PassTests.............</th>
<th colspan="6" class="header suite">
<div >
<a href="http://www.baidu.com">aaa</a>
</div>
</th>
</tr>
<tr class="columnHeadings">
<td> </td>
<th>testName</th>
<th>className</th>
<th>duration</th>
<th>params</th>
<th>description</th>
<th>output</th>
<th>dependMethod</th>
</tr> #foreach( $p in $pass)
<tr>
<td>$velocityCount</td>
<td>${p.testName}
#if(${p.description})
(${p.description})
#end</td>
<td>$p.className</td>
<td>$p.duration</td>
<td>$!p.params</td>
<td>$!p.description</td>
<td>
#foreach($o in $p.output)
$o<br/>
#end
</td>
<td>$p.dependMethod</td>
<td>$!p.throwable</td>
<td>
#if($p.throwable )
#foreach($o in $p.stackTrace)
$o<br/>
#end
#end
</td>
#end
</tr> </table>
<br/> <br/><br/> <table border="1">
<tr>
<th>FailedTests...............</th>
<th colspan="6" class="header suite">
<div >
<a href="http://www.baidu.com">aaa</a>
</div>
</th>
</tr>
<tr class="columnHeadings">
<td> </td>
<th>testName</th>
<th>className</th>
<th>duration</th>
<th>params</th>
<th>description</th>
<th>output</th>
<th>dependMethod</th>
<th>throwable</th>
<th>stackTrace</th>
</tr> #foreach( $p in $fail)
<tr>
<td>$velocityCount</td>
<td>$p.testName</td>
<td>$p.className</td>
<td>$p.duration</td>
<td>$!p.params</td>
<td>$!p.description</td>
<td>
#foreach($o in $p.output)
$o<br/>
#end
</td>
<td>$p.dependMethod</td>
<td>$p.throwable</td>
<td>
#if($p.throwable )
#foreach($o in $p.stackTrace)
$o<br/>
#end
#end
</td>
#end
</tr> </table> <br/><br/> </body>
</html>
7.测试报告,新建一个java项目,在测试类中添加监听器
@Listeners({com.reporter.main.GenerateReporter.class})
或者在testng.xml添加监听器
<listener class-name="com.reporter.main.GenerateReporter" />
import org.testng.Assert;
import org.testng.Reporter;
import org.testng.annotations.Listeners;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test; @Listeners({com.reporter.main.GenerateReporter.class})
public class test {
@Test
public void a(){
Reporter.log("<a href='http://www.baidu.com' target='blank'>baidu.com</a>");
System.out.println("111");
}
@Test(enabled=false,dependsOnMethods="a")
@Parameters("param")
public void b(String s){
Assert.assertEquals(s,"dataxml"); }
@Test(enabled=true,dependsOnMethods="e")
public void c(){
Assert.assertEquals(2,2);
}
@Test(description="测试方法 DDD")
public void d() {
Reporter.log("DDDDDDDDDD");
Reporter.log("AAAAAAAAAAAA");
System.out.println("Verify.verifyEquals(2,2)");
Assert.assertEquals(2,2);
}
@Test(description="98788",groups="test",invocationCount=1,dependsOnMethods="d")
public void e() {
Reporter.log("EEEEEEEEEEEEEEEEE");
Assert.assertEquals(1,2);
System.out.println("Verify.verifyEquals(2,2)");
}
}
testng生成自定义html报告的更多相关文章
- ExtentReports 结合 TestNg 生成自动化 html 报告 (支持多 suite)
转载:https://testerhome.com/topics/8134 重要说明:报告监听器源码修复一些bug,不再此处更新代码,最新代码可以到github查看最新报告监听器源码 前几天分享了ht ...
- testng生成报告ReportNG美化测试报告
testng生成报告ReportNG美化测试报告 testng生成报告ReportNG美化测试报告 ReportNG 是一个配合TestNG运行case后自动帮你在test-output文件内生成一个 ...
- testng生成报告 testng-xslt 美化测试报告
testng生成报告 testng-xslt 美化测试报告 testng生成报告 testng-xslt 美化测试报告 用TestNG测试后,自动会生成html的测试报告.利用 testNG-xslt ...
- 如何用 testNG 生成测试报告
原文地址https://testerhome.com/topics/3473 总结一下testNG生成报告的三种方式,基本都是我直接转载的,没有补充就不说了,有补充的我会加以说明的(这里直说生成报告, ...
- Jenkins 安装的HTML Publisher Plugin 插件无法展示ant生成的JunitReport报告
最近在做基于jenkins ant junit 的测试持续集成,单独ant junit生成的junitreport报告打开正常,使用Jenkins的HTML Publisher Plugin 插件无 ...
- JMeter学习-039-JMeter 3.0 生成 dashboard HTML 报告图表中文乱码
近期,经常有人问 JMeter 3.0 使用时,生成的 HTML 报告图表中的中文乱码问题.在此,简略的说一下解决的方法. 编码相关信息如下: 1.查看控制 csv.xml 等配置结果文件生成.读取的 ...
- Auty自动化测试框架第四篇——生成测试结果报告
[本文出自天外归云的博客园] 本次为Auty框架添加生成测试结果报告功能,文件结构更新:
- node生成自定义命令(yargs/commander)
第一部分可以生成一个自定义命令,例如常见的”express”,yargs和commander则可以在生成的自定义命令上做扩展,yargs将命令扩展成类似express --l xx的形式;而comma ...
- 通过用 .NET 生成自定义窗体设计器来定制应用程序
通过用 .NET 生成自定义窗体设计器来定制应用程序 https://www.microsoft.com/china/MSDN/library/netFramework/netframework/Cu ...
随机推荐
- AndroidStudio加快Gradle速度的方法-android study之旅(103)
方法1 打开setting,搜索compiler ,按照如图配置,不要问我为什么,宝宝心里苦~ 方法2 到开项目的根目录的gradle.properties ,把下面的注释解除 org.gradle. ...
- iPhone开发初探
本文是作者从一无所知到入门的知识学习过程,并结合自己在嵌入式开发的经验所写的技术总结文章,以供后来者学习. 苹果公司的iphone平台采用Object-c做为native language的开发,Ob ...
- ubuntu12.04:Mysql数据库:自动安装
打开终端,输入下面命令: 1 sudo apt-get install mysql-server 2 sudo apt-get install mysql-client 一旦安装完成,MySQL 服务 ...
- HDFS的java api操作
hdfs在生产应用中主要是针对客户端的开发,从hdfs提供的api中构造一个HDFS的访问客户端对象,然后通过该客户端对象操作(增删改查)HDFS上的文件. 搭建开发环境 方式一(windows环境下 ...
- Dubbo基本特性之泛化调用
Dubbo 是支持泛化调用的,什么是泛化调用呢?泛化调用的好处是什么呢,泛化调用说白一点就是服务消费者并没有服务的接口. 在<Dubbo入门-搭建一个最简单的Demo框架>一文中,我们已完 ...
- linux快速清空文件 比如log日志
linux中快速清空文件内容的几种方法这篇文章主要介绍了linux中快速清空文件内容的几种方法,需要的朋友可以参考下 权限要求: 至少执行用户对该文件有写的权限 --w------- 1 QA_Dep ...
- jsonp学习
使用 JSONP 实现跨域通信:http://www.ibm.com/developerworks/cn/web/wa-aj-jsonp1/
- Django中的原子事务相关注意事项
Django中的原子事务支持(transaction.atomic)方式函数装饰器或者with语句,这种方式特别是前者和spring里面的AOP事务支持方式基本等同,当然其实质方式都是原始的try.. ...
- Scala编程入门---面向对象编程之Trait
Scala中Trait是一种特殊概念 首先我们可以将Triat做为接口来使用,此时的Triat就与java中的接口非常相似 在Triat中可以定义抽象方法,就与抽象类中的抽象方法一样,只要不给出具体的 ...
- Java开源生鲜电商平台-盈利模式详解(源码可下载)
Java开源生鲜电商平台-盈利模式详解(源码可下载) 该平台提供一个联合买家与卖家的一个平台.(类似淘宝购物,这里指的是食材的购买.) 平台有以下的盈利模式:(类似的平台有美菜网,食材网等) 1. 订 ...