ExtentReport報告在Maven項目中的使用
1.首先在pom.xml文件中添加需要引入的ExtentReport測試報告相關的包。xml代碼如下
<dependencies> <dependency> <groupId>com.relevantcodes</groupId> <artifactId>extentreports</artifactId> <version>2.41.1</version> </dependency> <dependency> <groupId>com.vimalselvam</groupId> <artifactId>testng-extentsreport</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>com.aventstack</groupId> <artifactId>extentreports</artifactId> <version>3.0.6</version> </dependency> </dependencies>
2.ExtentReport的基本配置,在項目src->resources包下新建testng.xml 並配置監聽器<listeners> ,監聽器的xml代碼如下
<listeners>
<listener class-name="com.vimalselvam.testng.listener.ExtentTestNgFormatter"/>
</listeners>
3. test-output文件的生成,執行testng.xml后,項目會自動生成test-output文件夾,文件夾中存在測試報告的html頁面文件,如report.html 、emailable-report.html。
注意事項:此時如果直接通過瀏覽器訪問report.html 頁面,頁面樣式css會加載失敗。
4.解決測試報告樣式無法加載,需要重新寫一個類ExtentTestNGIReporterListener 去實現IReporter 。詳情代碼如下(代碼復制粘貼即可,網上也有很多)
5.修改testng中配置的<listeners>配置的類,testng.xml監聽器應該引入ExtentTestNGIReporterListener 類。
6.執行xml文件后,運行ExtentTestNGIReporterListener 生成的html頁面,index.html即可。
java代碼如下
1 package com.tester.extend.demo; 2 3 4 import com.aventstack.extentreports.ExtentReports; 5 import com.aventstack.extentreports.ExtentTest; 6 import com.aventstack.extentreports.ResourceCDN; 7 import com.aventstack.extentreports.Status; 8 import com.aventstack.extentreports.model.TestAttribute; 9 import com.aventstack.extentreports.reporter.ExtentHtmlReporter; 10 import com.aventstack.extentreports.reporter.configuration.ChartLocation; 11 import com.aventstack.extentreports.reporter.configuration.Theme; 12 import org.testng.*; 13 import org.testng.xml.XmlSuite; 14 15 import java.io.File; 16 import java.util.*; 17 18 public class ExtentTestNGIReporterListener implements IReporter { 19 //生成的路徑以及文件名 20 private static final String OUTPUT_FOLDER = "test-output/"; 21 private static final String FILE_NAME = "index.html"; 22 23 private ExtentReports extent; 24 25 @Override 26 public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) { 27 init(); 28 boolean createSuiteNode = false; 29 if (suites.size() > 1) { 30 createSuiteNode = true; 31 } 32 for (ISuite suite : suites) { 33 Map<String, ISuiteResult> result = suite.getResults(); 34 //如果suite里面沒有任何用例,直接跳過,不在報告里生成 35 if (result.size() == 0) { 36 continue; 37 } 38 //統計suite下的成功、失敗、跳過的總用例數 39 int suiteFailSize = 0; 40 int suitePassSize = 0; 41 int suiteSkipSize = 0; 42 ExtentTest suiteTest = null; 43 //存在多個suite的情況下,在報告中將同一個一個suite的測試結果歸為一類,創建一級節點。 44 if (createSuiteNode) { 45 suiteTest = extent.createTest(suite.getName()).assignCategory(suite.getName()); 46 } 47 boolean createSuiteResultNode = false; 48 if (result.size() > 1) { 49 createSuiteResultNode = true; 50 } 51 for (ISuiteResult r : result.values()) { 52 ExtentTest resultNode; 53 ITestContext context = r.getTestContext(); 54 if (createSuiteResultNode) { 55 //沒有創建suite的情況下,將在SuiteResult的創建為一級節點,否則創建為suite的一個子節點。 56 if (null == suiteTest) { 57 resultNode = extent.createTest(r.getTestContext().getName()); 58 } else { 59 resultNode = suiteTest.createNode(r.getTestContext().getName()); 60 } 61 } else { 62 resultNode = suiteTest; 63 } 64 if (resultNode != null) { 65 resultNode.getModel().setName(suite.getName() + " : " + r.getTestContext().getName()); 66 if (resultNode.getModel().hasCategory()) { 67 resultNode.assignCategory(r.getTestContext().getName()); 68 } else { 69 resultNode.assignCategory(suite.getName(), r.getTestContext().getName()); 70 } 71 resultNode.getModel().setStartTime(r.getTestContext().getStartDate()); 72 resultNode.getModel().setEndTime(r.getTestContext().getEndDate()); 73 //統計SuiteResult下的數據 74 int passSize = r.getTestContext().getPassedTests().size(); 75 int failSize = r.getTestContext().getFailedTests().size(); 76 int skipSize = r.getTestContext().getSkippedTests().size(); 77 suitePassSize += passSize; 78 suiteFailSize += failSize; 79 suiteSkipSize += skipSize; 80 if (failSize > 0) { 81 resultNode.getModel().setStatus(Status.FAIL); 82 } 83 resultNode.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;", passSize, failSize, skipSize)); 84 } 85 buildTestNodes(resultNode, context.getFailedTests(), Status.FAIL); 86 buildTestNodes(resultNode, context.getSkippedTests(), Status.SKIP); 87 buildTestNodes(resultNode, context.getPassedTests(), Status.PASS); 88 } 89 if (suiteTest != null) { 90 suiteTest.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;", suitePassSize, suiteFailSize, suiteSkipSize)); 91 if (suiteFailSize > 0) { 92 suiteTest.getModel().setStatus(Status.FAIL); 93 } 94 } 95 96 } 97 // for (String s : Reporter.getOutput()) { 98 // extent.setTestRunnerOutput(s); 99 // } 100 101 extent.flush(); 102 } 103 104 private void init() { 105 //文件夾不存在的話進行創建 106 File reportDir = new File(OUTPUT_FOLDER); 107 if (!reportDir.exists() && !reportDir.isDirectory()) { 108 reportDir.mkdir(); 109 } 110 ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME); 111 // 設置靜態文件的DNS 112 //怎么樣解決cdn.rawgit.com訪問不了的情況 (CSS無法訪問) 113 htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS); 114 //以上這句話 就是解決CSS無法加載 115 htmlReporter.config().setDocumentTitle("api自動化測試報告"); 116 htmlReporter.config().setReportName("api自動化測試報告"); 117 htmlReporter.config().setChartVisibilityOnOpen(true); 118 htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP); 119 htmlReporter.config().setTheme(Theme.STANDARD); 120 htmlReporter.config().setCSS(".node.level-1 ul{ display:none;} .node.level-1.active ul{display:block;}"); 121 extent = new ExtentReports(); 122 extent.attachReporter(htmlReporter); 123 extent.setReportUsesManualConfiguration(true); 124 } 125 126 private void buildTestNodes(ExtentTest extenttest, IResultMap tests, Status status) { 127 //存在父節點時,獲取父節點的標簽 128 String[] categories = new String[0]; 129 if (extenttest != null) { 130 List<TestAttribute> categoryList = extenttest.getModel().getCategoryContext().getAll(); 131 categories = new String[categoryList.size()]; 132 for (int index = 0; index < categoryList.size(); index++) { 133 categories[index] = categoryList.get(index).getName(); 134 } 135 } 136 137 ExtentTest test; 138 139 if (tests.size() > 0) { 140 //調整用例排序,按時間排序 141 Set<ITestResult> treeSet = new TreeSet<ITestResult>(new Comparator<ITestResult>() { 142 @Override 143 public int compare(ITestResult o1, ITestResult o2) { 144 return o1.getStartMillis() < o2.getStartMillis() ? -1 : 1; 145 } 146 }); 147 treeSet.addAll(tests.getAllResults()); 148 for (ITestResult result : treeSet) { 149 Object[] parameters = result.getParameters(); 150 String name = ""; 151 //如果有參數,則使用參數的toString組合代替報告中的name 152 for (Object param : parameters) { 153 name += param.toString(); 154 } 155 if (name.length() > 0) { 156 if (name.length() > 50) { 157 name = name.substring(0, 49) + "..."; 158 } 159 } else { 160 name = result.getMethod().getMethodName(); 161 } 162 if (extenttest == null) { 163 test = extent.createTest(name); 164 } else { 165 //作為子節點進行創建時,設置同父節點的標簽一致,便於報告檢索。 166 test = extenttest.createNode(name).assignCategory(categories); 167 } 168 //test.getModel().setDescription(description.toString()); 169 //test = extent.createTest(result.getMethod().getMethodName()); 170 for (String group : result.getMethod().getGroups()) 171 test.assignCategory(group); 172 173 List<String> outputList = Reporter.getOutput(result); 174 for (String output : outputList) { 175 //將用例的log輸出報告中 176 test.debug(output); 177 } 178 if (result.getThrowable() != null) { 179 test.log(status, result.getThrowable()); 180 } else { 181 test.log(status, "Test " + status.toString().toLowerCase() + "ed"); 182 } 183 184 test.getModel().setStartTime(getTime(result.getStartMillis())); 185 test.getModel().setEndTime(getTime(result.getEndMillis())); 186 } 187 } 188 } 189 190 private Date getTime(long millis) { 191 Calendar calendar = Calendar.getInstance(); 192 calendar.setTimeInMillis(millis); 193 return calendar.getTime(); 194 } 195 }
testng.xml代碼如下
<?xml version="1.0" encoding="UTF-8" ?> <suite name="我自己的接口測試套件"> <test name="這些事測試的模塊"> <classes> <class name="com.tester.extend.demo.TestMethodsDomo"/> <methods> <include name="test1"/> <include name="test2"/> <include name="test3"/> <include name="logoDemo"/> </methods> </classes> </test> <listeners> <!-- <listener class-name="com.vimalselvam.testng.listener.ExtentTestNgFormatter"/>--> <listener class-name="com.tester.extend.demo.ExtentTestNGIReporterListener"/> <!-- 以上listener在為了讓測試報告樣式css能夠重新加載而寫的java類--> </listeners> </suite>
報告截圖如下