ExtentReport测试报告的使用
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>
报告截图如下