之前用testNG自带的test-outputemailable-report.html,做出的UI自动化测试报告,页面不太好看。

在网上找到一个新的报告编写,自己尝试了一下,埋了一些坑,修改了输出时间格式,最终出的结果比以前稍好。

简单介绍下Velocity

1.不用像jsp那样编译成servlet(.Class)文件,直接装载后就可以运行了,装载的过程在web.xml里面配置。【后缀名为.vhtml是我们自己的命名方式。也只有在这里配置了哪种类型的文件,那么这种类型的文件才能解析velocity语法】

2.web页面上可以很方便的调用java后台的方法,不管方法是静态的还是非静态的。只需要在toolbox.xml里面把类配置进去就可以咯。【调用的方法 $class.method()】即可。

3.可以使用模版生成静态文档html【特殊情况下才用】

需要下载两个war包,

testng-6.9.9.jar,velocity-1.7.jar【亲测之后发现velocity-1.7.jar会报错,建议用velocity-dep-1.4.jar,因为后者包含了三个war包的内容(commons-collections-3.2.1.jar、commons-lang-2.4.jar和oro-2.0.8.jar)】

百度网盘贡献路径如下:

链接:https://pan.baidu.com/s/1FeN-di2T_nMo3ahHQ91xtg 密码:2nb7

DataBean.java

package main.java.baseReport;

import org.testng.ITestNGMethod;

import java.util.Collection;
import java.util.List; 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 starttime; //
private String endtime; //
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 String getStarttime() {
return starttime;
} public void setStarttime(String starttime) {
this.starttime = starttime;
} public String getEndtime() {
return endtime;
} public void setEndtime(String endtime) {
this.endtime = endtime;
} 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;
} }

  

GenerateReporter
package main.java.baseReport;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.testng.*;
import org.testng.xml.XmlSuite; import java.io.*;
import java.util.List;
import java.util.Map;
import java.util.Properties; import static java.lang.System.out; 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("main/java/baseReport/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();//测试跳过的测试方法
//IResultMap starttime=testContext.getStartDate();
//IResultMap endtime=testContext.getEndDate(); context.put("pass", data.testResults(passedTests, ITestResult.SUCCESS));
context.put("fail", data.testResults(failedTests, ITestResult.FAILURE));
context.put("skip", data.testResults(skippedTests, ITestResult.FAILURE));
}
}
// 输出流 OutputStream out = new FileOutputStream("report.html");
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();
}
}
}

 Overview.vm

<?xml version="1.0" encoding="utf-8" ?>

<head>
<title>Test Report</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="description" content="TestNG unit test results." /> </head>
<body> <h1>IKEA Web Automatic Test</h1>
<table border="1">
<tr>
<th>OverView........</th>
<th colspan="6" class="header suite">
<div >
<a href="http://www.baidu.com">Detail Information for test cases</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>StartTime(S)</th>
<th>EndTime(S)</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.starttime</td>
<td>$overView.endtime</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">Detail Information for test cases</a>
</div>
</th>
</tr>
<tr class="columnHeadings">
<td> </td>
<th>testName</th>
<th>className</th>
<th>starttime</th>
<th>endtime</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.starttime</td>
<td>$p.endtime</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">Detail Information for test cases</a>
</div>
</th>
</tr>
<tr class="columnHeadings">
<td> </td>
<th>testName</th>
<th>className</th>
<th>StartTime</th>
<th>EndTime</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.starttime</td>
<td>$p.endtime</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>

  

ReporterData
package main.java.baseReport;

import org.testng.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*; 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) throws ParseException {
// 测试结果汇总数据
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.setStarttime(units.getStarttime(context));
data.setEndtime(units.getEndTime(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(); SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
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.setStarttime(formatter.format(result.getEndMillis()));
data.setEndtime(formatter.format(result.getEndMillis()));
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;
}
}

  

ReportUnits
package main.java.baseReport;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
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%");
SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
/**
*测试消耗时长
*return 秒,保留3位小数
*/
public String getTestDuration(ITestContext context){
long duration;
duration=context.getEndDate().getTime()-context.getStartDate().getTime();
return formatDuration(duration);
} public String getStarttime(ITestContext context) throws ParseException {
return formatter.format(context.getStartDate());
} public String getEndTime(ITestContext context){
return formatter.format(context.getEndDate());
//return context.getStartDate().toString();
} 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();
}
}

  

TestResultSort
package main.java.baseReport;

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开始时间排序
}
}

  

优缺点比较:

1. 多个测试类一起运行,通过配置testng.xml 一起运行的时候,testNG自带的eport会将多个测试文件显示在同一个报表中,并且根据测试类进行分门别类;自己编写的velocity显示的报表,目前是一个测试类出一个报表,很尴尬。

只显示了最后一个测试类,并且覆盖了前面的

测试报告 之 testNG + Velocity 编写自定义html测试报告的更多相关文章

  1. testng生成报告ReportNG美化测试报告

    testng生成报告ReportNG美化测试报告 testng生成报告ReportNG美化测试报告 ReportNG 是一个配合TestNG运行case后自动帮你在test-output文件内生成一个 ...

  2. testng生成报告 testng-xslt 美化测试报告

    testng生成报告 testng-xslt 美化测试报告 testng生成报告 testng-xslt 美化测试报告 用TestNG测试后,自动会生成html的测试报告.利用 testNG-xslt ...

  3. 用mel编写自定义节点的属性编辑器界面

    用mel编写自定义节点的属性编辑器界面比较麻烦,而且网上例子又少,下面给出一个范例,说明基本的格式 // 初始化节点时调用 global proc initControl(string $attrNa ...

  4. django “如何”系列4:如何编写自定义模板标签和过滤器

    django的模板系统自带了一系列的内建标签和过滤器,一般情况下可以满足你的要求,如果觉得需更精准的模板标签或者过滤器,你可以自己编写模板标签和过滤器,然后使用{% load %}标签使用他们. 代码 ...

  5. RobotFramework自动化测试框架-使用Python编写自定义的RobotFramework Lib

    使用Python构建Lib工程 可以用来开发Python Lib的IDE工具有很多,常见的有Pycharm,Eclipse with PyDev插件等,而且在RobotFramework官网中也已经提 ...

  6. SpringBoot编写自定义的starter 专题

    What’s in a name All official starters follow a similar naming pattern; spring-boot-starter-*, where ...

  7. SpringBoot编写自定义配置信息

    ⒈编写自定义配置类 1.浏览器配置 package cn.coreqi.security.properties; public class BrowserProperties { private St ...

  8. R语言-编写自定义函数 ZZ

    一.函数构造器 每一个R函数都包括三个部分:函数名,程序主体以及参数集合,在编写自定义R函数时,需要将三个部分各自储存在一个R对象中.这里需要使用function函数,形如: my_function& ...

  9. Django 编写自定义的 404 / 500 报错界面

    Django 编写自定义的 404 / 500 报错界面 1. 首先 setting.py 文件中的 debug 参数设置成 false ,不启用调试. DEBUG = False 2. 在 temp ...

随机推荐

  1. 搭建私有yum仓库

    需要工具: centos7 nginx rsync 新建文件夹存放镜像数据 mkdir /data mkdir /data/yum_data 同步数据 国内开源镜像站没有几个支持rsync,科技大学的 ...

  2. 机器学习理论基础学习13--- 隐马尔科夫模型 (HMM)

    隐含马尔可夫模型并不是俄罗斯数学家马尔可夫发明的,而是美国数学家鲍姆提出的,隐含马尔可夫模型的训练方法(鲍姆-韦尔奇算法)也是以他名字命名的.隐含马尔可夫模型一直被认为是解决大多数自然语言处理问题最为 ...

  3. javascript实现unicode与字符互相转换

    javascript实现unicode与字符互相转换. <script language="javascript">  //手机检测  function checkMo ...

  4. Twitter OA prepare: Equilibrium index of an array

    Equilibrium index of an array is an index such that the sum of elements at lower indexes is equal to ...

  5. 摘要JSR168 PORLET标准手册汉化整理

    本规范汉化资源搜集整理于网上并由我作了些修改和添加,主要为适应大陆的语辞.用语及其他未译之处. 由于本人于水平有限,如有错误,请各位高手指正:若有高见,希望不吝言辞,同为中国开源作项献. 特此严重感谢 ...

  6. mysql5.7服务器The innodb_system data file 'ibdata1' must be writable导致无法启动服务器

    现象如下:The innodb_system data file 'ibdata1' must be writable. 解决方案如下: 1.关闭mysqld进程: 2.删除配置文件中datadir所 ...

  7. 终端FQ

    前言我一直以为全局FQ就是所有的都能够自由访问,没想到终端有时候却不行,这里终端一般指的是window的cmd和mac的Terminal.终端一般程序员用的比较多,下载第三方依赖啊,都需要,所以我总结 ...

  8. Python:键盘输入input

    从键盘读入数据 >>> num=input('利润是:') 利润是:55 >>>

  9. python 处理xml

    XML XML指可扩展标记语言(Extensible Markup Language) XML被设计用于结构化.存储和传输数据 XML是一种标记语言,很类似于HTML XML没有像HTML那样 ...

  10. Python之路----递归函数

    1.小练一下 用map来处理字符串列表,把列表中所有人都变成sb,比方alex_sb name=['alex','wupeiqi','yuanhao','nezha'] # def func(item ...