转自:https://blog.csdn.net/kdslkd/article/details/51198433
testng原生的或reportng的报告总有些不符合需要,尝试生成自定义测试报告,
用到的依赖包:testng-6.9.9.jar,velocity-1.7.jar
1.定义一个DataBean,保存需要收集的数据
只定义部分数据,比如suite、testname、groups等好多数据还没,需要用到的时候再加了
1 packagecom.reporter.main;2
3 importjava.util.Collection;4 importjava.util.List;5
6 importorg.testng.ITestNGMethod;7
8 public classDataBean {9 private int excludeTestsSize; //未执行的test数量
10 private int passedTestsSize; //测试通过的数量
11 private int failedTestsSize; //测试失败的数量
12 private int skippedTestsSize; //测试跳过的数量
13 private int allTestsSize; //全部执行的测试的数量
14 private ITestNGMethod[] allTestsMethod; //全部执行的测试方法
15 private Collection excludeTestsMethod; //未执行的测试方法
16 private String testsTime; //测试耗时
17 private String passPercent; //测试通过率
18 private String testName; //测试方法名
19 private String className; //测试类名
20 private String duration; //单个测试周期
21 private String params; //测试用参数
22 private String description; //测试描述
23 private List output; //Reporter Output
24 private String dependMethod; //测试依赖方法
25 private Throwable throwable; //测试异常原因
26 private StackTraceElement[] stackTrace; //异常堆栈信息
27
28 public intgetExcludeTestsSize() {29 returnexcludeTestsSize;30 }31
32 public void setExcludeTestsSize(intexcludeTestsSize) {33 this.excludeTestsSize =excludeTestsSize;34 }35
36 public intgetPassedTestsSize() {37 returnpassedTestsSize;38 }39
40 public void setPassedTestsSize(intpassedTestsSize) {41 this.passedTestsSize =passedTestsSize;42 }43
44 public intgetFailedTestsSize() {45 returnfailedTestsSize;46 }47
48 public void setFailedTestsSize(intfailedTestsSize) {49 this.failedTestsSize =failedTestsSize;50 }51
52 public intgetSkippedTestsSize() {53 returnskippedTestsSize;54 }55
56 public void setSkippedTestsSize(intskippedTestsSize) {57 this.skippedTestsSize =skippedTestsSize;58 }59
60 public intgetAllTestsSize() {61 returnallTestsSize;62 }63
64 public void setAllTestsSize(intallTestsSize) {65 this.allTestsSize =allTestsSize;66 }67
68 publicString getPassPercent() {69 returnpassPercent;70 }71
72 public voidsetPassPercent(String passPercent) {73 this.passPercent =passPercent;74 }75
76 publicString getTestName() {77 returntestName;78 }79
80 public voidsetTestName(String testName) {81 this.testName =testName;82 }83
84 publicString getClassName() {85 returnclassName;86 }87
88 public voidsetClassName(String className) {89 this.className =className;90 }91
92 publicString getDuration() {93 returnduration;94 }95
96 public voidsetDuration(String duration) {97 this.duration =duration;98 }99
100 publicString getParams() {101 returnparams;102 }103
104 public voidsetParams(String params) {105 this.params =params;106 }107
108 publicString getDescription() {109 returndescription;110 }111
112 public voidsetDescription(String description) {113 this.description =description;114 }115
116 public ListgetOutput() {117 returnoutput;118 }119
120 public void setOutput(Listoutput) {121 this.output =output;122 }123
124 publicString getDependMethod() {125 returndependMethod;126 }127
128 public voidsetDependMethod(String dependMethod) {129 this.dependMethod =dependMethod;130 }131
132 publicThrowable getThrowable() {133 returnthrowable;134 }135
136 public voidsetThrowable(Throwable throwable2) {137 this.throwable =throwable2;138 }139
140 publicStackTraceElement[] getStackTrace() {141 returnstackTrace;142 }143
144 public voidsetStackTrace(StackTraceElement[] stackTrace) {145 this.stackTrace =stackTrace;146 }147
148 public voidsetTestsTime(String testsTime) {149 this.testsTime =testsTime;150 }151
152 publicString getTestsTime() {153 returntestsTime;154 }155
156 public voidsetAllTestsMethod(ITestNGMethod[] allTestsMethod) {157 this.allTestsMethod =allTestsMethod;158 }159
160 publicITestNGMethod[] getAllTestsMethod() {161 returnallTestsMethod;162 }163
164 public void setExcludeTestsMethod(CollectionexcludeTestsMethod) {165 this.excludeTestsMethod =excludeTestsMethod;166 }167
168 public CollectiongetExcludeTestsMethod() {169 returnexcludeTestsMethod;170 }171
172 }
2.对需要特别处理的报告元素,比如测试周期、通过率
1 packagecom.reporter.main;2
3 importjava.text.DecimalFormat;4 importjava.text.NumberFormat;5 importjava.util.ArrayList;6 importjava.util.Arrays;7 importjava.util.Collection;8 importjava.util.Iterator;9 importjava.util.List;10 importorg.testng.ITestContext;11 importorg.testng.ITestResult;12 importorg.testng.Reporter;13
14 public classReportUnits {15 private static final NumberFormat DURATION_FORMAT = new DecimalFormat("#0.000");16 private static final NumberFormat PERCENTAGE_FORMAT = new DecimalFormat("#0.00%");17 /**
18 *测试消耗时长19 *return 秒,保留3位小数20 */
21 publicString getTestDuration(ITestContext context){22 longduration;23 duration=context.getEndDate().getTime()-context.getStartDate().getTime();24 returnformatDuration(duration);25 }26
27 public String formatDuration(longelapsed)28 {29 double seconds = (double) elapsed / 1000;30 returnDURATION_FORMAT.format(seconds);31 }32 /**
33 *测试通过率34 *return 2.22%,保留2位小数35 */
36 public String formatPercentage(int numerator, intdenominator)37 {38 return PERCENTAGE_FORMAT.format(numerator / (double) denominator);39 }40
41 /**
42 * 获取方法参数,以逗号分隔43 *@paramresult44 *@return
45 */
46 publicString getParams(ITestResult result){47 Object[] params =result.getParameters();48 List list = new ArrayList(params.length);49 for(Object o:params){50 list.add(renderArgument(o));51 }52 returncommaSeparate(list);53 }54 /**
55 * 获取依赖的方法56 *@paramresult57 *@return
58 */
59 publicString getDependMethods(ITestResult result){60 String[] methods=result.getMethod().getMethodsDependedUpon();61 returncommaSeparate(Arrays.asList(methods));62 }63 /**
64 * 堆栈轨迹,暂不确定怎么做,放着先65 *@paramthrowable66 *@return
67 */
68 publicString getCause(Throwable throwable){69 StackTraceElement[] stackTrace=throwable.getStackTrace(); //堆栈轨迹
70 List list = new ArrayList(stackTrace.length);71 for(Object o:stackTrace){72 list.add(renderArgument(o));73 }74 returncommaSeparate(list);75 }76 /**
77 * 获取全部日志输出信息78 *@return
79 */
80 public ListgetAllOutput(){81 returnReporter.getOutput();82 }83
84 /**
85 * 按testresult获取日志输出信息86 *@paramresult87 *@return
88 */
89 public ListgetTestOutput(ITestResult result){90 returnReporter.getOutput(result);91 }92
93
94 /*将object 转换为String*/
95 privateString renderArgument(Object argument)96 {97 if (argument == null)98 {99 return "null";100 }101 else if (argument instanceofString)102 {103 return "\"" + argument + "\"";104 }105 else if (argument instanceofCharacter)106 {107 return "\'" + argument + "\'";108 }109 else
110 {111 returnargument.toString();112 }113 }114 /*将集合转换为以逗号分隔的字符串*/
115 private String commaSeparate(Collectionstrings)116 {117 StringBuilder buffer = newStringBuilder();118 Iterator iterator =strings.iterator();119 while(iterator.hasNext())120 {121 String string =iterator.next();122 buffer.append(string);123 if(iterator.hasNext())124 {125 buffer.append(", ");126 }127 }128 returnbuffer.toString();129 }130 }
3.测试方法排序,按测试方法执行时间排序
遍历 suites 得到的getAllResults()是一个set 集合,需要对数据进行排序
这里是将getAllResults()转为list,实现Comparable接口的方法进行排序的.
(好像是可以将getAllResults()转成TreeSet排序?)
1 packagecom.reporter.main;2
3 importorg.testng.ITestResult;4
5 public class TestResultSort implements Comparable{6 privateLong order;7 @Override8 public intcompareTo(ITestResult arg0) {9 //TODO Auto-generated method stub
10 return this.order.compareTo( arg0.getStartMillis());//按test开始时间排序
11 }12
13 }
4.得到测试报告数据
1 packageorg.reporter.main;2
3
4 importjava.util.ArrayList;5 importjava.util.Collection;6 importjava.util.Collections;7 importjava.util.List;8 importjava.util.Set;9
10
11 importorg.testng.IResultMap;12 importorg.testng.ITestContext;13 importorg.testng.ITestNGMethod;14 importorg.testng.ITestResult;15 importorg.testng.Reporter;16
17
18
19
20 public classReporterData {21 //测试结果Set转为list,再按执行时间排序 ,返回list
22 public List sortByTime(Setstr) {23 List list = new ArrayList();24 for(ITestResult r : str) {25 list.add(r);26 }27 Collections.sort(list);28 returnlist;29
30
31 }32
33 publicDataBean testContext(ITestContext context) {34 //测试结果汇总数据
35 DataBean data = newDataBean();36 ReportUnits units = newReportUnits();37 IResultMap passedTests =context.getPassedTests();38 IResultMap failedTests=context.getFailedTests();39 IResultMap skipedTests =context.getSkippedTests();40 //全部测试周期方法,包括beforetest,beforeclass,beforemethod,aftertest,afterclass,aftermethod41 //IResultMap passedConfigurations =context.getPassedConfigurations();42 //IResultMap failedConfigurations =context.getFailedConfigurations();43 //IResultMap skipedConfigurations =context.getSkippedConfigurations();
44 Collection excludeTests =context.getExcludedMethods();45
46 int passedTestsSize =passedTests.size();47 int failedTestsSize =failedTests.size();48 int skipedTestsSize =skipedTests.size();49 int excludeTestsSize =excludeTests.size();50 //所有测试结果的数量=测试pass+fail+skip的和,因为数据驱动一个测试方法有多次执行的可能,导致方法总数并不等于测试总数
51 int allTestsSize= passedTestsSize+failedTestsSize+skipedTestsSize;52 data.setAllTestsSize(allTestsSize);53 data.setPassedTestsSize(passedTestsSize);54 data.setFailedTestsSize(failedTestsSize);55 data.setSkippedTestsSize(skipedTestsSize);56 data.setExcludeTestsSize(excludeTestsSize);57 data.setTestsTime(units.getTestDuration(context));58 data.setPassPercent(units.formatPercentage(passedTestsSize, allTestsSize));59 data.setAllTestsMethod(context.getAllTestMethods());60 data.setExcludeTestsMethod(context.getExcludedMethods());61
62 returndata;63
64
65 }66
67
68 public List testResults(IResultMap map, intstatus) {69 //测试结果详细数据
70 List list = new ArrayList();71 ReportUnits units = newReportUnits();72 map.getAllResults().size();73 for(ITestResult result : sortByTime(map.getAllResults())) {74 DataBean data = newDataBean();75 data.setTestName(result.getName());76 data.setClassName(result.getTestClass().getName());77 data.setDuration(units.formatDuration(result.getEndMillis()78 -result.getStartMillis()));79 data.setParams(units.getParams(result));80 data.setDescription(result.getMethod().getDescription());81 data.setOutput(Reporter.getOutput(result));82 data.setDependMethod(units.getDependMethods(result));83 data.setThrowable(result.getThrowable());84 if (result.getThrowable() != null) {85 data.setStackTrace(result.getThrowable().getStackTrace());86 }87 list.add(data);88 }89 returnlist;90 }91
92 }
5.生成测试报告,生成的测试报告是项目根目录下的report.html(要定制的话再改了)
使用 IReporter 监听器。IReporter 监听器只有一个方法需要实现。
void generateReport(java.util.List xmlSuites, java.util.List
suites, java.lang.String outputDirectory)
该方法在所有测试方法执行结束后被调用,通过遍历 xmlSuites 和 suites 能够获取所有测试方法的信息以及测试结果。outputDirectory 是默认的测试报表生成路径,当然你可以指定其他路径生成报表。
1 packagecom.reporter.main;2
3 importjava.io.BufferedWriter;4 importjava.io.FileWriter;5 importjava.io.Writer;6 importjava.util.List;7 importjava.util.Map;8 importjava.util.Properties;9 importorg.apache.velocity.Template;10 importorg.apache.velocity.VelocityContext;11 importorg.apache.velocity.app.VelocityEngine;12 importorg.testng.IReporter;13 importorg.testng.IResultMap;14 importorg.testng.ISuite;15 importorg.testng.ISuiteResult;16 importorg.testng.ITestContext;17 importorg.testng.ITestResult;18 importorg.testng.xml.XmlSuite;19
20 public class GenerateReporter implementsIReporter {21 @Override22 public void generateReport(List xmlSuites, Listsuites,23 String outputDirectory) {24 //TODO Auto-generated method stub
25 try{26 //初始化并取得Velocity引擎
27 VelocityEngine ve = newVelocityEngine();28 Properties p = newProperties();29 //虽然不懂为什么这样设置,但结果是好的.可以用了
30 p.setProperty("resource.loader", "class");31 p.setProperty("class.resource.loader.class","org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");32 ve.init(p);33 Template t = ve.getTemplate("com/reporter/VMmodel/overview.vm");34 VelocityContext context = newVelocityContext();35
36 for(ISuite suite : suites) {37 Map suiteResults =suite.getResults();38 for(ISuiteResult suiteResult : suiteResults.values()) {39 ReporterData data = newReporterData();40 ITestContext testContext =suiteResult.getTestContext();41 //把数据填入上下文
42 context.put("overView", data.testContext(testContext));//测试结果汇总信息43 //ITestNGMethod[] allTests = testContext.getAllTestMethods();//所有的测试方法44 //Collection excludeTests = testContext.getExcludedMethods();//未执行的测试方法
45 IResultMap passedTests = testContext.getPassedTests();//测试通过的测试方法
46 IResultMap failedTests = testContext.getFailedTests();//测试失败的测试方法
47 IResultMap skippedTests = testContext.getSkippedTests();//测试跳过的测试方法
48
49 context.put("pass", data.testResults(passedTests, ITestResult.SUCCESS));50 context.put("fail", data.testResults(failedTests, ITestResult.FAILURE));51 context.put("skip", data.testResults(skippedTests, ITestResult.FAILURE));52
53
54
55 }56 }57 //输出流
58 //Writer writer = new BufferedWriter(new FileWriter("report.html"));
59 OutputStream out=new FileOutputStream("report.html");60 Writer writer = new BufferedWriter(new OutputStreamWriter(out,"utf-8"));//解决乱码问题61 //转换输出
62 t.merge(context, writer);63 //System.out.println(writer.toString());
64 writer.flush();65 } catch(Exception e) {66 //TODO Auto-generated catch block
67 e.printStackTrace();68 }69 }70
71
72 }
6.测试报告模板,文件后辍为vm,比如overview.vm,内容差不多就是一个html文件,(要漂亮报告找前端。。。)
1 <?xml version="1.0" encoding="utf-8"?>
2
3
4
test5
6
7
8
9
10
11
Test
12
13
14
OverView........15
16
17 aaa
18
19
20
21
22
23
all24
excluded25
passed26
faild27
skipped28
duration(S)29
passration30
alltestMethod31
excluedMethod32
33
34
35
TestResult36
$overView.allTestsSize37
$overView.excludeTestsSize38
$overView.passedTestsSize39
$overView.failedTestsSize40
$overView.skippedTestsSize41
$overView.testsTime42
$overView.passPercent43
44 #foreach($p in $overView.allTestsMethod)45 $p
46 #end47
48
49 #foreach($e in $overView.excludeTestsMethod)50 $e
51 #end52
53
54
55
56
57
58
PassTests.............59
60
61 aaa
62
63
64
65
66
67
testName68
className69
duration70
params71
description72
output73
dependMethod74
75
76 #foreach( $p in $pass)77
78
$velocityCount79
${p.testName}80 #if(${p.description})81 (${p.description})82 #end83
$p.className84
$p.duration85
$!p.params86
$!p.description87
88 #foreach($o in $p.output)89 $o
90 #end91
92
$p.dependMethod93
$!p.throwable94
95 #if($p.throwable )96 #foreach($o in $p.stackTrace)97 $o
98 #end99 #end100
101 #end102
103
104
105
106
107
108
109
110
111
112
FailedTests...............113
114
115 aaa
116
117
118
119
120
121
testName122
className123
duration124
params125
description126
output127
dependMethod128
throwable129
stackTrace130
131
132 #foreach( $p in $fail)133
134
$velocityCount135
$p.testName136
$p.className137
$p.duration138
$!p.params139
$!p.description140
141 #foreach($o in $p.output)142 $o
143 #end144
145
$p.dependMethod146
$p.throwable147
148 #if($p.throwable )149 #foreach($o in $p.stackTrace)150 $o
151 #end152 #end153
154 #end155
156
157
158
159
160
161
162
163
164
165