java自定义测试报告,测试报告 之 testNG + Velocity 编写自定义html测试报告

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

fce821428a8e913844fe66446d3b4bb6.png

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

2999efa039d0fb830581d46f3447fb2d.png

简单介绍下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)】

百度网盘贡献路径如下:

9e04fdcc22158c7cb76100851cf5aa8e.png

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 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 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 getOutput() {

return output;

}

public void setOutput(List 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 excludeTestsMethod) {

this.excludeTestsMethod = excludeTestsMethod;

}

public Collection 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 xmlSuites, List 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 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 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

Test Report

IKEA Web Automatic Test

OverView........
allexcludedpassedfaildskippedStartTime(S)EndTime(S)duration(S)passrationalltestMethodexcluedMethod
TestResult$overView.allTestsSize$overView.excludeTestsSize$overView.passedTestsSize$overView.failedTestsSize$overView.skippedTestsSize$overView.starttime$overView.endtime$overView.testsTime$overView.passPercent

#foreach($p in $overView.allTestsMethod)

$p

#end

#foreach($e in $overView.excludeTestsMethod)

$e

#end

PassTests.............
testNameclassNamestarttimeendtimedurationparamsdescriptionoutputdependMethod

#foreach( $p in $pass)

$velocityCount${p.testName}

#if(${p.description})

(${p.description})

#end

$p.className$p.starttime$p.endtime$p.duration$!p.params$!p.description

#foreach($o in $p.output)

$o

#end

$p.dependMethod$!p.throwable

#if($p.throwable )

#foreach($o in $p.stackTrace)

$o

#end

#end

#end

FailedTests...............
testNameclassNameStartTimeEndTimedurationparamsdescriptionoutputdependMethodthrowablestackTrace

#foreach( $p in $fail)

$velocityCount$p.testName$p.className$p.starttime$p.endtime$p.duration$!p.params$!p.description

#foreach($o in $p.output)

$o

#end

$p.dependMethod$p.throwable

#if($p.throwable )

#foreach($o in $p.stackTrace)

$o

#end

#end

#end

ReporterData

package main.java.baseReport;

import org.testng.*;

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.*;

public class ReporterData {

// 测试结果Set转为list,再按执行时间排序 ,返回list

public List sortByTime(Set str) {

List list = new ArrayList();

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 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 testResults(IResultMap map, int status) {

// 测试结果详细数据

List list = new ArrayList();

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 list = new ArrayList(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 list = new ArrayList(stackTrace.length);

for (Object o:stackTrace){

list.add(renderArgument(o));

}

return commaSeparate(list);

}

/**

* 获取全部日志输出信息

* @return

*/

public List getAllOutput(){

return Reporter.getOutput();

}

/**

* 按testresult获取日志输出信息

* @param result

* @return

*/

public List 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 strings)

{

StringBuilder buffer = new StringBuilder();

Iterator 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 {

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显示的报表,目前是一个测试类出一个报表,很尴尬。

4e6cb83fd54b1f3e6c92f9ea64dac3ca.png

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

f05e9fa76cd91e7509249b17fffa19f1.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值