引言
自动化测试一个漂亮的测试 告是必不可少的。世面上主流的测试 告框架是Extent Report和Allure Report。两个都特别好,今天介绍是Extent Report。Extent Report有一些功能是收费的,比如:邮件 告。但是通常免费的功能已经满足我们需求了。
依赖
创建一个Maven项目。添加如下依赖:
<dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.14.3</version> </dependency> <dependency> <groupId>com.relevantcodes</groupId> <artifactId>extentreports</artifactId> <version>2.41.2</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>
新建测试用例
新建一个TestNG测试类,随便写几条测试用例。
public class DemoTest{ @Test public void test1(){ Assert.assertEquals(1,2); } @Test public void test2(){ Assert.assertEquals(1,1); } @Test public void test3(){ Assert.assertEquals("aaa","aaa"); } @Test public void logDemo(){ Reporter.log("这是我自己写的日志"); throw new RuntimeException("这是我自己运行时候的异常"); }}
Listener
Testng的高级功能listener,不了解的话可以看一下官方文档。我们这里创建一个listener用来生成测试 告。代码如下:
package com.demo.listeners;import com.aventstack.extentreports.ExtentReports;import com.aventstack.extentreports.ExtentTest;import com.aventstack.extentreports.ResourceCDN;import com.aventstack.extentreports.Status;?import com.aventstack.extentreports.model.TestAttribute;import com.aventstack.extentreports.reporter.ExtentHtmlReporter;import com.aventstack.extentreports.reporter.configuration.ChartLocation;import com.aventstack.extentreports.reporter.configuration.Theme;import org.testng.*;import org.testng.xml.XmlSuite;?import java.io.File;import java.util.*;?public class ExtentTestNGIReporterListener implements IReporter { //生成的路径以及文件名 private static final String OUTPUT_FOLDER = "test-output/"; private static final String FILE_NAME = "index.html";? private ExtentReports extent;? @Override public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) { init(); boolean createSuiteNode = false; if(suites.size()>1){ createSuiteNode=true; } for (ISuite suite : suites) { Map<String, ISuiteResult> result = suite.getResults(); //如果suite里面没有任何用例,直接跳过,不在 告里生成 if(result.size()==0){ continue; } //统计suite下的成功、失败、跳过的总用例数 int suiteFailSize=0; int suitePassSize=0; int suiteSkipSize=0; ExtentTest suiteTest=null; //存在多个suite的情况下,在 告中将同一个一个suite的测试结果归为一类,创建一级节点。 if(createSuiteNode){ suiteTest = extent.createTest(suite.getName()).assignCategory(suite.getName()); } boolean createSuiteResultNode = false; if(result.size()>1){ createSuiteResultNode=true; } for (ISuiteResult r : result.values()) { ExtentTest resultNode; ITestContext context = r.getTestContext(); if(createSuiteResultNode){ //没有创建suite的情况下,将在SuiteResult的创建为一级节点,否则创建为suite的一个子节点。 if( null == suiteTest){ resultNode = extent.createTest(r.getTestContext().getName()); }else{ resultNode = suiteTest.createNode(r.getTestContext().getName()); } }else{ resultNode = suiteTest; } if(resultNode != null){ resultNode.getModel().setName(suite.getName()+" : "+r.getTestContext().getName()); if(resultNode.getModel().hasCategory()){ resultNode.assignCategory(r.getTestContext().getName()); }else{ resultNode.assignCategory(suite.getName(),r.getTestContext().getName()); } resultNode.getModel().setStartTime(r.getTestContext().getStartDate()); resultNode.getModel().setEndTime(r.getTestContext().getEndDate()); //统计SuiteResult下的数据 int passSize = r.getTestContext().getPassedTests().size(); int failSize = r.getTestContext().getFailedTests().size(); int skipSize = r.getTestContext().getSkippedTests().size(); suitePassSize += passSize; suiteFailSize += failSize; suiteSkipSize += skipSize; if(failSize>0){ resultNode.getModel().setStatus(Status.FAIL); } resultNode.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",passSize,failSize,skipSize)); } buildTestNodes(resultNode,context.getFailedTests(), Status.FAIL); buildTestNodes(resultNode,context.getSkippedTests(), Status.SKIP); buildTestNodes(resultNode,context.getPassedTests(), Status.PASS); } if(suiteTest!= null){ suiteTest.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",suitePassSize,suiteFailSize,suiteSkipSize)); if(suiteFailSize>0){ suiteTest.getModel().setStatus(Status.FAIL); } }? }// for (String s : Reporter.getOutput()) {// extent.setTestRunnerOutput(s);// }? extent.flush(); }? private void init() { //文件夹不存在的话进行创建 File reportDir= new File(OUTPUT_FOLDER); if(!reportDir.exists()&& !reportDir .isDirectory()){ reportDir.mkdir(); } ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME); // 设置静态文件的DNS //怎么样解决cdn.rawgit.com访问不了的情况 htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);? htmlReporter.config().setDocumentTitle("api自动化测试 告"); htmlReporter.config().setReportName("api自动化测试 告"); htmlReporter.config().setChartVisibilityOnOpen(true); htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP); htmlReporter.config().setTheme(Theme.STANDARD); //htmlReporter.config().setEncoding("gb2312"); htmlReporter.config().setCSS(".node.level-1 ul{ display:none;} .node.level-1.active ul{display:block;}"); extent = new ExtentReports(); extent.attachReporter(htmlReporter); extent.setReportUsesManualConfiguration(true); }? private void buildTestNodes(ExtentTest extenttest, IResultMap tests, Status status) { //存在父节点时,获取父节点的标签 String[] categories=new String[0]; if(extenttest != null ){ List<TestAttribute> categoryList = extenttest.getModel().getCategoryContext().getAll(); categories = new String[categoryList.size()]; for(int index=0;index<categoryList.size();index++){ categories[index] = categoryList.get(index).getName(); } }? ExtentTest test;? if (tests.size() > 0) { //调整用例排序,按时间排序 Set<ITestResult> treeSet = new TreeSet<ITestResult>(new Comparator<ITestResult>() { @Override public int compare(ITestResult o1, ITestResult o2) { return o1.getStartMillis()<o2.getStartMillis()?-1:1; } }); treeSet.addAll(tests.getAllResults()); for (ITestResult result : treeSet) { Object[] parameters = result.getParameters(); String name=""; //如果有参数,则使用参数的toString组合代替 告中的name for(Object param:parameters){ name+=param.toString(); } if(name.length()>0){ if(name.length()>50){ name= name.substring(0,49)+"..."; } }else{ name = result.getMethod().getMethodName(); } if(extenttest==null){ test = extent.createTest(name); }else{ //作为子节点进行创建时,设置同父节点的标签一致,便于 告检索。 test = extenttest.createNode(name).assignCategory(categories); } //test.getModel().setDescription(description.toString()); //test = extent.createTest(result.getMethod().getMethodName()); for (String group : result.getMethod().getGroups()) test.assignCategory(group);? List<String> outputList = Reporter.getOutput(result); for(String output:outputList){ //将用例的log输出 告中 test.debug(output); } if (result.getThrowable() != null) { test.log(status, result.getThrowable()); } else { test.log(status, "Test " + status.toString().toLowerCase() + "ed"); }? test.getModel().setStartTime(getTime(result.getStartMillis())); test.getModel().setEndTime(getTime(result.getEndMillis())); } } }? private Date getTime(long millis) { Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(millis); return calendar.getTime(); }}?
Suite文件
在Resources文件夹创建一个TestNG的Suite文件叫testng.xml。内容如下:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"><suite name="Suite"> <listeners> <listener class-name="com.demo.listeners.ExtentTestNGIReporterListener"></listener> </listeners> <test name="Test"> <classes> <class name="com.demo.tests.DemoTest" /> </classes> </test></suite> ?
此时我们的项目目录结构如下:
执行右键testng.xml,执行测试用例。在test-output下会生成一个index.html测试 告。
告打开如下:
结语
Extent Report还提供了自己的 告服务器ExtentX,可以把每次执行后 告都存在服务器上。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!