轉自:https://blog.csdn.net/kdslkd/article/details/51198433html
testng原生的或reportng的報告總有些不符合須要,嘗試生成自定義測試報告,
用到的依賴包:testng-6.9.9.jar,velocity-1.7.jar
1.定義一個DataBean,保存須要收集的數據前端
只定義部分數據,好比suite、testname、groups等好多數據還沒,須要用到的時候再加了java
1 package com.reporter.main; 2 3 import java.util.Collection; 4 import java.util.List; 5 6 import org.testng.ITestNGMethod; 7 8 public class DataBean { 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<ITestNGMethod> 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<String> output; //Reporter Output 24 private String dependMethod; //測試依賴方法 25 private Throwable throwable; //測試異常緣由 26 private StackTraceElement[] stackTrace; // 異常堆棧信息 27 28 public int getExcludeTestsSize() { 29 return excludeTestsSize; 30 } 31 32 public void setExcludeTestsSize(int excludeTestsSize) { 33 this.excludeTestsSize = excludeTestsSize; 34 } 35 36 public int getPassedTestsSize() { 37 return passedTestsSize; 38 } 39 40 public void setPassedTestsSize(int passedTestsSize) { 41 this.passedTestsSize = passedTestsSize; 42 } 43 44 public int getFailedTestsSize() { 45 return failedTestsSize; 46 } 47 48 public void setFailedTestsSize(int failedTestsSize) { 49 this.failedTestsSize = failedTestsSize; 50 } 51 52 public int getSkippedTestsSize() { 53 return skippedTestsSize; 54 } 55 56 public void setSkippedTestsSize(int skippedTestsSize) { 57 this.skippedTestsSize = skippedTestsSize; 58 } 59 60 public int getAllTestsSize() { 61 return allTestsSize; 62 } 63 64 public void setAllTestsSize(int allTestsSize) { 65 this.allTestsSize = allTestsSize; 66 } 67 68 public String getPassPercent() { 69 return passPercent; 70 } 71 72 public void setPassPercent(String passPercent) { 73 this.passPercent = passPercent; 74 } 75 76 public String getTestName() { 77 return testName; 78 } 79 80 public void setTestName(String testName) { 81 this.testName = testName; 82 } 83 84 public String getClassName() { 85 return className; 86 } 87 88 public void setClassName(String className) { 89 this.className = className; 90 } 91 92 public String getDuration() { 93 return duration; 94 } 95 96 public void setDuration(String duration) { 97 this.duration = duration; 98 } 99 100 public String getParams() { 101 return params; 102 } 103 104 public void setParams(String params) { 105 this.params = params; 106 } 107 108 public String getDescription() { 109 return description; 110 } 111 112 public void setDescription(String description) { 113 this.description = description; 114 } 115 116 public List<String> getOutput() { 117 return output; 118 } 119 120 public void setOutput(List<String> output) { 121 this.output = output; 122 } 123 124 public String getDependMethod() { 125 return dependMethod; 126 } 127 128 public void setDependMethod(String dependMethod) { 129 this.dependMethod = dependMethod; 130 } 131 132 public Throwable getThrowable() { 133 return throwable; 134 } 135 136 public void setThrowable(Throwable throwable2) { 137 this.throwable = throwable2; 138 } 139 140 public StackTraceElement[] getStackTrace() { 141 return stackTrace; 142 } 143 144 public void setStackTrace(StackTraceElement[] stackTrace) { 145 this.stackTrace = stackTrace; 146 } 147 148 public void setTestsTime(String testsTime) { 149 this.testsTime = testsTime; 150 } 151 152 public String getTestsTime() { 153 return testsTime; 154 } 155 156 public void setAllTestsMethod(ITestNGMethod[] allTestsMethod) { 157 this.allTestsMethod = allTestsMethod; 158 } 159 160 public ITestNGMethod[] getAllTestsMethod() { 161 return allTestsMethod; 162 } 163 164 public void setExcludeTestsMethod(Collection<ITestNGMethod> excludeTestsMethod) { 165 this.excludeTestsMethod = excludeTestsMethod; 166 } 167 168 public Collection<ITestNGMethod> getExcludeTestsMethod() { 169 return excludeTestsMethod; 170 } 171 172 }
2.對須要特別處理的報告元素,好比測試周期、經過率apache
1 package com.reporter.main; 2 3 import java.text.DecimalFormat; 4 import java.text.NumberFormat; 5 import java.util.ArrayList; 6 import java.util.Arrays; 7 import java.util.Collection; 8 import java.util.Iterator; 9 import java.util.List; 10 import org.testng.ITestContext; 11 import org.testng.ITestResult; 12 import org.testng.Reporter; 13 14 public class ReportUnits { 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 public String getTestDuration(ITestContext context){ 22 long duration; 23 duration=context.getEndDate().getTime()-context.getStartDate().getTime(); 24 return formatDuration(duration); 25 } 26 27 public String formatDuration(long elapsed) 28 { 29 double seconds = (double) elapsed / 1000; 30 return DURATION_FORMAT.format(seconds); 31 } 32 /** 33 *測試經過率 34 *return 2.22%,保留2位小數 35 */ 36 public String formatPercentage(int numerator, int denominator) 37 { 38 return PERCENTAGE_FORMAT.format(numerator / (double) denominator); 39 } 40 41 /** 42 * 獲取方法參數,以逗號分隔 43 * @param result 44 * @return 45 */ 46 public String getParams(ITestResult result){ 47 Object[] params = result.getParameters(); 48 List<String> list = new ArrayList<String>(params.length); 49 for (Object o:params){ 50 list.add(renderArgument(o)); 51 } 52 return commaSeparate(list); 53 } 54 /** 55 * 獲取依賴的方法 56 * @param result 57 * @return 58 */ 59 public String getDependMethods(ITestResult result){ 60 String[] methods=result.getMethod().getMethodsDependedUpon(); 61 return commaSeparate(Arrays.asList(methods)); 62 } 63 /** 64 * 堆棧軌跡,暫不肯定怎麼作,放着先 65 * @param throwable 66 * @return 67 */ 68 public String getCause(Throwable throwable){ 69 StackTraceElement[] stackTrace=throwable.getStackTrace(); //堆棧軌跡 70 List<String> list = new ArrayList<String>(stackTrace.length); 71 for (Object o:stackTrace){ 72 list.add(renderArgument(o)); 73 } 74 return commaSeparate(list); 75 } 76 /** 77 * 獲取所有日誌輸出信息 78 * @return 79 */ 80 public List<String> getAllOutput(){ 81 return Reporter.getOutput(); 82 } 83 84 /** 85 * 按testresult獲取日誌輸出信息 86 * @param result 87 * @return 88 */ 89 public List<String> getTestOutput(ITestResult result){ 90 return Reporter.getOutput(result); 91 } 92 93 94 /*將object 轉換爲String*/ 95 private String renderArgument(Object argument) 96 { 97 if (argument == null) 98 { 99 return "null"; 100 } 101 else if (argument instanceof String) 102 { 103 return "\"" + argument + "\""; 104 } 105 else if (argument instanceof Character) 106 { 107 return "\'" + argument + "\'"; 108 } 109 else 110 { 111 return argument.toString(); 112 } 113 } 114 /*將集合轉換爲以逗號分隔的字符串*/ 115 private String commaSeparate(Collection<String> strings) 116 { 117 StringBuilder buffer = new StringBuilder(); 118 Iterator<String> 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 return buffer.toString(); 129 } 130 }
3.測試方法排序,按測試方法執行時間排序
遍歷 suites 獲得的getAllResults()是一個set 集合,須要對數據進行排序
這裏是將getAllResults()轉爲list,實現Comparable接口的方法進行排序的.app
(好像是能夠將getAllResults()轉成TreeSet排序?)ide
1 package com.reporter.main; 2 3 import org.testng.ITestResult; 4 5 public class TestResultSort implements Comparable<ITestResult> { 6 private Long order; 7 @Override 8 public int compareTo(ITestResult arg0) { 9 // TODO Auto-generated method stub 10 return this.order.compareTo( arg0.getStartMillis());//按test開始時間排序 11 } 12 13 }
4.獲得測試報告數據測試
1 package org.reporter.main; 2 3 4 import java.util.ArrayList; 5 import java.util.Collection; 6 import java.util.Collections; 7 import java.util.List; 8 import java.util.Set; 9 10 11 import org.testng.IResultMap; 12 import org.testng.ITestContext; 13 import org.testng.ITestNGMethod; 14 import org.testng.ITestResult; 15 import org.testng.Reporter; 16 17 18 19 20 public class ReporterData { 21 // 測試結果Set<ITestResult>轉爲list,再按執行時間排序 ,返回list 22 public List<ITestResult> sortByTime(Set<ITestResult> str) { 23 List<ITestResult> list = new ArrayList<ITestResult>(); 24 for (ITestResult r : str) { 25 list.add(r); 26 } 27 Collections.sort(list); 28 return list; 29 30 31 } 32 33 public DataBean testContext(ITestContext context) { 34 // 測試結果彙總數據 35 DataBean data = new DataBean(); 36 ReportUnits units = new ReportUnits(); 37 IResultMap passedTests = context.getPassedTests(); 38 IResultMap failedTests= context.getFailedTests(); 39 IResultMap skipedTests = context.getSkippedTests(); 40 //所有測試周期方法,包括beforetest,beforeclass,beforemethod,aftertest,afterclass,aftermethod 41 //IResultMap passedConfigurations =context.getPassedConfigurations(); 42 //IResultMap failedConfigurations =context.getFailedConfigurations(); 43 //IResultMap skipedConfigurations =context.getSkippedConfigurations(); 44 Collection<ITestNGMethod> 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 return data; 63 64 65 } 66 67 68 public List<DataBean> testResults(IResultMap map, int status) { 69 // 測試結果詳細數據 70 List<DataBean> list = new ArrayList<DataBean>(); 71 ReportUnits units = new ReportUnits(); 72 map.getAllResults().size(); 73 for (ITestResult result : sortByTime(map.getAllResults())) { 74 DataBean data = new DataBean(); 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 return list; 90 } 91 92 }
5.生成測試報告,生成的測試報告是項目根目錄下的report.html(要定製的話再改了)
使用 IReporter 監聽器。IReporter 監聽器只有一個方法須要實現。
void generateReport(java.util.List<XmlSuite> xmlSuites, java.util.List
<ISuite> suites, java.lang.String outputDirectory)ui
該方法在全部測試方法執行結束後被調用,經過遍歷 xmlSuites 和 suites 可以獲取全部測試方法的信息以及測試結果。outputDirectory 是默認的測試報表生成路徑,固然你能夠指定其餘路徑生成報表。this
1 package com.reporter.main; 2 3 import java.io.BufferedWriter; 4 import java.io.FileWriter; 5 import java.io.Writer; 6 import java.util.List; 7 import java.util.Map; 8 import java.util.Properties; 9 import org.apache.velocity.Template; 10 import org.apache.velocity.VelocityContext; 11 import org.apache.velocity.app.VelocityEngine; 12 import org.testng.IReporter; 13 import org.testng.IResultMap; 14 import org.testng.ISuite; 15 import org.testng.ISuiteResult; 16 import org.testng.ITestContext; 17 import org.testng.ITestResult; 18 import org.testng.xml.XmlSuite; 19 20 public class GenerateReporter implements IReporter { 21 @Override 22 public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, 23 String outputDirectory) { 24 // TODO Auto-generated method stub 25 try { 26 // 初始化並取得Velocity引擎 27 VelocityEngine ve = new VelocityEngine(); 28 Properties p = new Properties(); 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 = new VelocityContext(); 35 36 for (ISuite suite : suites) { 37 Map<String, ISuiteResult> suiteResults = suite.getResults(); 38 for (ISuiteResult suiteResult : suiteResults.values()) { 39 ReporterData data = new ReporterData(); 40 ITestContext testContext = suiteResult.getTestContext(); 41 // 把數據填入上下文 42 context.put("overView", data.testContext(testContext));//測試結果彙總信息 43 //ITestNGMethod[] allTests = testContext.getAllTestMethods();//全部的測試方法 44 //Collection<ITestNGMethod> 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 <span style="white-space:pre"> </span>//Writer writer = new BufferedWriter(new FileWriter("report.html")); 59 <span style="white-space:pre"> </span>OutputStream out=new FileOutputStream("report.html"); 60 <span style="white-space:pre"> </span>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文件,(要漂亮報告找前端。。。)spa
1 <?xml version="1.0" encoding="utf-8" ?> 2 3 <head> 4 <title>test</title> 5 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> 6 <meta name="description" content="TestNG unit test results." /> 7 8 </head> 9 <body> 10 11 <h1>Test</h1> 12 <table border="1"> 13 <tr> 14 <th>OverView........</th> 15 <th colspan="6" class="header suite"> 16 <div > 17 <a href="http://www.baidu.com">aaa</a> 18 </div> 19 </th> 20 </tr> 21 <tr class="columnHeadings"> 22 <td> </td> 23 <th>all</th> 24 <th>excluded</th> 25 <th>passed</th> 26 <th>faild</th> 27 <th>skipped</th> 28 <th>duration(S)</th> 29 <th>passration</th> 30 <th>alltestMethod</th> 31 <th>excluedMethod</th> 32 </tr> 33 34 <tr> 35 <td>TestResult</td> 36 <td>$overView.allTestsSize</td> 37 <td>$overView.excludeTestsSize</td> 38 <td>$overView.passedTestsSize</td> 39 <td>$overView.failedTestsSize</td> 40 <td>$overView.skippedTestsSize</td> 41 <td>$overView.testsTime</td> 42 <td>$overView.passPercent</td> 43 <td> 44 #foreach($p in $overView.allTestsMethod) 45 $p<br/> 46 #end 47 </td> 48 <td> 49 #foreach($e in $overView.excludeTestsMethod) 50 $e<br/> 51 #end 52 </td> 53 </tr> 54 </table> 55 <br/><br/> 56 <table border="1"> 57 <tr> 58 <th>PassTests.............</th> 59 <th colspan="6" class="header suite"> 60 <div > 61 <a href="http://www.baidu.com">aaa</a> 62 </div> 63 </th> 64 </tr> 65 <tr class="columnHeadings"> 66 <td> </td> 67 <th>testName</th> 68 <th>className</th> 69 <th>duration</th> 70 <th>params</th> 71 <th>description</th> 72 <th>output</th> 73 <th>dependMethod</th> 74 </tr> 75 76 #foreach( $p in $pass) 77 <tr> 78 <td>$velocityCount</td> 79 <td>${p.testName} 80 #if(${p.description}) 81 (${p.description}) 82 #end</td> 83 <td>$p.className</td> 84 <td>$p.duration</td> 85 <td>$!p.params</td> 86 <td>$!p.description</td> 87 <td> 88 #foreach($o in $p.output) 89 $o<br/> 90 #end 91 </td> 92 <td>$p.dependMethod</td> 93 <td>$!p.throwable</td> 94 <td> 95 #if($p.throwable ) 96 #foreach($o in $p.stackTrace) 97 $o<br/> 98 #end 99 #end 100 </td> 101 #end 102 </tr> 103 104 </table> 105 <br/> 106 107 108 <br/><br/> 109 110 <table border="1"> 111 <tr> 112 <th>FailedTests...............</th> 113 <th colspan="6" class="header suite"> 114 <div > 115 <a href="http://www.baidu.com">aaa</a> 116 </div> 117 </th> 118 </tr> 119 <tr class="columnHeadings"> 120 <td> </td> 121 <th>testName</th> 122 <th>className</th> 123 <th>duration</th> 124 <th>params</th> 125 <th>description</th> 126 <th>output</th> 127 <th>dependMethod</th> 128 <th>throwable</th> 129 <th>stackTrace</th> 130 </tr> 131 132 #foreach( $p in $fail) 133 <tr> 134 <td>$velocityCount</td> 135 <td>$p.testName</td> 136 <td>$p.className</td> 137 <td>$p.duration</td> 138 <td>$!p.params</td> 139 <td>$!p.description</td> 140 <td> 141 #foreach($o in $p.output) 142 $o<br/> 143 #end 144 </td> 145 <td>$p.dependMethod</td> 146 <td>$p.throwable</td> 147 <td> 148 #if($p.throwable ) 149 #foreach($o in $p.stackTrace) 150 $o<br/> 151 #end 152 #end 153 </td> 154 #end 155 </tr> 156 157 158 </table> 159 160 <br/><br/> 161 162 163 164 </body> 165 </html>
7.測試報告,新建一個java項目,在測試類中添加監聽器
@Listeners({com.reporter.main.GenerateReporter.class})
或者在testng.xml添加監聽器
<listener class-name="com.reporter.main.GenerateReporter" />
1 import org.testng.Assert; 2 import org.testng.Reporter; 3 import org.testng.annotations.Listeners; 4 import org.testng.annotations.Parameters; 5 import org.testng.annotations.Test; 6 7 @Listeners({com.reporter.main.GenerateReporter.class}) 8 public class test { 9 @Test 10 public void a(){ 11 Reporter.log("<a href='http://www.baidu.com' target='blank'>baidu.com</a>"); 12 System.out.println("111"); 13 } 14 @Test(enabled=false,dependsOnMethods="a") 15 @Parameters("param") 16 public void b(String s){ 17 Assert.assertEquals(s,"dataxml"); 18 19 } 20 @Test(enabled=true,dependsOnMethods="e") 21 public void c(){ 22 Assert.assertEquals(2,2); 23 } 24 @Test(description="測試方法 DDD") 25 public void d() { 26 Reporter.log("DDDDDDDDDD"); 27 Reporter.log("AAAAAAAAAAAA"); 28 System.out.println("Verify.verifyEquals(2,2)"); 29 Assert.assertEquals(2,2); 30 } 31 @Test(description="98788",groups="test",invocationCount=1,dependsOnMethods="d") 32 public void e() { 33 Reporter.log("EEEEEEEEEEEEEEEEE"); 34 Assert.assertEquals(1,2); 35 System.out.println("Verify.verifyEquals(2,2)"); 36 } 37 }