温州网站建设优化/微信管理系统登录入口
TestNG与ExtentReport集成
目录
1 通过实现ITestListener的方法添加Reporter log
1.1 MyTestListener设置
1.2 输出结果
2 TestNG与ExtentReporter集成
2.1 项目结构
2.2 MyExtentReportListener设置
2.3 单多Suite、Test组合测试
2.3.1 单Suite单Test
2.3.2 单Suite多Test
2.3.3 多Suite
源代码:interface-test-framework.zip
1 通过实现ITestListener的方法添加Reporter log
TestNG的Listener列表
TestNG提供了一组预定义的Listener Java接口,这些接口全部继承自TestNG的 ITestNGListener接口。用户创建这些接口的实现类,并把它们加入到 TestNG 中,TestNG便会在测试运行的不同时刻调用这些类中的接口方法:
- IExecutionListener 监听TestNG运行的启动和停止。
- IAnnotationTransformer 注解转换器,用于TestNG测试类中的注解。
- ISuiteListener 测试套件监听器,监听测试套件的启动和停止。
- ITestListener 测试方法执行监听。
- IConfigurationListener 监听配置方法相关的接口。
- IMethodInterceptor 拦截器,调整测试方法的执行顺序。
- IInvokedMethodListener 测试方法拦截监听,用于获取被TestNG调用的在Method的Before 和After方法监听器。该方法只会被配置和测试方法调用。
- IHookable 若测试类实现了该接口,当@Test方法被发现时,它的run()方法将会被调用来替代@Test方法。这个测试方法通常在IHookCallBack的callback之上调用,比较适用于需要JASS授权的测试类。
- IReporter 实现该接口可以生成一份测试报告。
本文将着重介绍最常用到的两个Listener ITestListener与Ireporter接口。
1.1 MyTestListener设置
通过实现ITestListener的方法,添加Reporter.log
MyTestListener.java
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import org.testng.Reporter;public class MyTestListener implements ITestListener {//用例执行结束后,用例执行成功时调用public void onTestSuccess(ITestResult tr) {logTestEnd(tr, "Success");}//用例执行结束后,用例执行失败时调用public void onTestFailure(ITestResult tr) {logTestEnd(tr, "Failed");}//用例执行结束后,用例执行skip时调用public void onTestSkipped(ITestResult tr) {logTestEnd(tr, "Skipped");}//每次方法失败但是已经使用successPercentage进行注释时调用,并且此失败仍保留在请求的成功百分比之内。public void onTestFailedButWithinSuccessPercentage(ITestResult tr) {logTestEnd(tr, "FailedButWithinSuccessPercentage");}//每次调用测试@Test之前调用public void onTestStart(ITestResult result) {logTestStart(result);}//在测试类被实例化之后调用,并在调用任何配置方法之前调用。public void onStart(ITestContext context) {return;}//在所有测试运行之后调用,并且所有的配置方法都被调用public void onFinish(ITestContext context) {return;}// 在用例执行结束时,打印用例的执行结果信息protected void logTestEnd(ITestResult tr, String result) {Reporter.log(String.format("-------------Result: %s-------------", result), true);}// 在用例开始时,打印用例的一些信息,比如@Test对应的方法名,用例的描述等等protected void logTestStart(ITestResult tr) {Reporter.log(String.format("-------------Run: %s.%s---------------", tr.getTestClass().getName(), tr.getMethod().getMethodName()), true);Reporter.log(String.format("用例描述: %s, 优先级: %s", tr.getMethod().getDescription(), tr.getMethod().getPriority()),true);return;}
}
1.2 输出结果
SmkDemo1.java
import com.demo.listener.MyTestListener;
import org.testng.Assert;
import org.testng.Reporter;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;@Listeners({MyTestListener.class})
public class SmkDemo1 {@Test(description="测testPass11的描述",priority = 1,groups = {"分组1"})public void testPass11(){Reporter.log("Test11的第一步",true);Assert.assertEquals(1,1);}
}
输出界面显示如下
图1 log to 输出界面
2 TestNG与ExtentReporter集成
2.1 项目结构
图2 项目结构
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.demo</groupId><artifactId>interface-test-framework</artifactId><version>1.0-SNAPSHOT</version><dependencies><!-- 测试报告插件和testng的结合 --><dependency><groupId>com.vimalselvam</groupId><artifactId>testng-extentsreport</artifactId><version>1.3.1</version></dependency><!-- extentreports测试报告插件 --><dependency><groupId>com.aventstack</groupId><artifactId>extentreports</artifactId><version>3.0.6</version></dependency><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>7.1.0</version></dependency></dependencies>
</project>
2.2 MyExtentReportListener设置
MyExtentReporterListener.java
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.ResourceCDN;
import com.aventstack.extentreports.Status;
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 MyExtentReporterListener implements IReporter {//生成的路径以及文件名private static final String OUTPUT_FOLDER = "test-output/";private static final String FILE_NAME = "report.html";private ExtentReports extent;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());suiteTest = extent.createTest(suite.getName());}boolean createSuiteResultNode = false;if(result.size()>1){createSuiteResultNode=true;}for (ISuiteResult r : result.values()) {ExtentTest resultNode=null;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;}String[] categories=new String[1];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());categories[0]=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,categories,context.getFailedTests(), Status.FAIL);buildTestNodes(resultNode,categories,context.getSkippedTests(), Status.SKIP);buildTestNodes(resultNode,categories,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访问不了的问题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().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, String[] categories, 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>() {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组合代替报告中的namefor(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();}
}
2.3 单多Suite、Test组合测试
2.3.1 单Suite单Test
testngSingleSuiteSingleTest.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="SingleSuite"><test name="SingleTest" verbose="1" preserve-order="true" ><classes><class name="com.demo.testcase.smoke.SmkDemo1"></class><class name="com.demo.testcase.sit.SitDemo2"></class></classes></test><!--配置监听器--><listeners><listener class-name="com.demo.listener.MyTestListener"/><listener class-name="com.demo.listener.MyExtentReporterListener"/></listeners>
</suite>
图3 单Suite单Test总览
图4 单Suite单Test分组
图5 单Suite单Test错误分组
图6 单Suite单Test Dashboard
2.3.2 单Suite多Test
testngSingleSuiteDoubleTest.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="SingleSuite"><test name="DoubleTest1" verbose="1" preserve-order="true" ><classes><class name="com.demo.testcase.smoke.SmkDemo1"></class></classes></test><test name="DoubleTest2" verbose="1" preserve-order="true" ><classes><class name="com.demo.testcase.sit.SitDemo2"></class></classes></test><!--配置监听器--><listeners><listener class-name="com.demo.listener.MyTestListener"/><listener class-name="com.demo.listener.MyExtentReporterListener"/></listeners>
</suite>
图7 单Suite多Test总览
图8 单Suite多Test分组
2.3.3 多Suite
testngDoubleSuite.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="DoubleSuite"><suite-files><suite-file path="testngSingleSuiteSingleTest.xml"/><suite-file path="testngSingleSuiteDoubleTest.xml"/></suite-files><!--配置监听器--><listeners><listener class-name="com.demo.listener.MyTestListener"/><listener class-name="com.demo.listener.MyExtentReporterListener"/></listeners>
</suite>
图9 多Suite总览1
图10 多Suite总览2
图11 多Suite分组
参考
[1] testng框架Listener介绍及测试结果的收集
[2] TestNG执行的日志ITestListener与结果IReporter
[3] TestNG执行的日志ITestListener与结果IReporter
[4] TestNg Beginner’s Guide–阅后总结之Textng.xml
[5] TestNg Beginner’s Guide–阅后总结之TestNg注解
ExtentReports 另一种方法
引言
在走进Java接口测试之测试框架TestNG 中我们详细介绍了 TestNG 的各种用法, 在本文中,我将详细介绍如何将 ExtentReports 测试报告与TestNG集成。
ExtentReports 简介
主要特点:
- 生成的报告简洁美观
- 生成的单html方便 Jenkins 集成发邮件
- 自带集中展示历史报告的服务端
- 支持 Java 和 .Net
TestNG 原生报告有点丑,信息整理有点乱。ExtentReports 是用于替换TestNG 原生报告。当然也可以使用 ReportNg,个人偏好 ExtentReports 样式。
官网已经给了很多demo了,大家可以参考练习。
官网:http://extentreports.com/
客户端:
https://github.com/anshooarora/extentreports-java/commits/master
服务端:https://github.com/anshooarora/extentx
Step-1:添加 Maven 依赖包
引入pom.xml
<!--引入extentreports相关包--><dependency><groupId>com.aventstack</groupId><artifactId>extentreports</artifactId><version>3.1.5</version><scope>provided</scope></dependency><dependency><groupId>com.vimalselvam</groupId><artifactId>testng-extentsreport</artifactId><version>1.3.1</version></dependency><dependency><groupId>com.relevantcodes</groupId><artifactId>extentreports</artifactId><version>2.41.2</version></dependency><!--引入testng测试框架--><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>6.14.3</version><scope>compile</scope></dependency>
Step-2:重写 ExtentTestNgFormatter 类
主要基于以下两项原因:
- 支持报告中展示更多状态类型的测试结果,例如:成功、失败、警告、跳过等。
- 因为不支持cdn.rawgit.com访问,故替css访问方式。
创建 MyExtentTestNgFormatter 类
下载 ExtentReportes 源码,找到 ExtentTestNgFormatter 类,Listener 目录下创建 MyExtentTestNgFormatter.java
类直接继承 ExtentTestNgFormatter 类。
public class MyExtentTestNgFormatter extends ExtentTestNgFormatter {
解决CDN无法访问
构造方法加入
htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);
具体代码如下:
public MyExtentTestNgFormatter() {setInstance(this);testRunnerOutput = new ArrayList<>();String reportPathStr = System.getProperty("reportPath");File reportPath;try {reportPath = new File(reportPathStr);} catch (NullPointerException e) {reportPath = new File(TestNG.DEFAULT_OUTPUTDIR);}if (!reportPath.exists()) {if (!reportPath.mkdirs()) {throw new RuntimeException("Failed to create output run directory");}}File reportFile = new File(reportPath, "report.html");File emailReportFile = new File(reportPath, "emailable-report.html");htmlReporter = new ExtentHtmlReporter(reportFile);EmailReporter emailReporter = new EmailReporter(emailReportFile);reporter = new ExtentReports();// 如果cdn.rawgit.com访问不了,可以设置为:ResourceCDN.EXTENTREPORTS或者ResourceCDN.GITHUBhtmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);reporter.attachReporter(htmlReporter, emailReporter);}
重写 onstart 方法
新建一个类名为MyReporter,一个静态ExtentTest的引用。
Listener 包下 MyReporter.java
public class MyReporter { public static ExtentTest report; }
MyExtentTestNgFormatter.java
public void onStart(ITestContext iTestContext) {ISuite iSuite = iTestContext.getSuite();ExtentTest suite = (ExtentTest) iSuite.getAttribute(SUITE_ATTR);ExtentTest testContext = suite.createNode(iTestContext.getName());// 将MyReporter.report静态引用赋值为testContext。// testContext是@Test每个测试用例时需要的。report.log可以跟随具体的测试用例。另请查阅源码。MyReporter.report = testContext;iTestContext.setAttribute("testContext", testContext);}
自定义配置
测试报告默认是在工程根目录下创建 test-output/
文件夹下,名为 report.html
、 emailable-report.html
。可根据各自需求在构造方法中修改。
public MyExtentTestNgFormatter() {setInstance(this);testRunnerOutput = new ArrayList<>();// reportPath报告路径String reportPathStr = System.getProperty("reportPath");File reportPath;try {reportPath = new File(reportPathStr);} catch (NullPointerException e) {reportPath = new File(TestNG.DEFAULT_OUTPUTDIR);}if (!reportPath.exists()) {if (!reportPath.mkdirs()) {throw new RuntimeException("Failed to create output run directory");}}// 报告名report.htmlFile reportFile = new File(reportPath, "report.html");// 邮件报告名emailable-report.htmlFile emailReportFile = new File(reportPath, "emailable-report.html");htmlReporter = new ExtentHtmlReporter(reportFile);EmailReporter emailReporter = new EmailReporter(emailReportFile);reporter = new ExtentReports();reporter.attachReporter(htmlReporter, emailReporter);}
report.log
report.log 支持多种玩法
// 根据状态不同添加报告。型如警告 MyReporter.report.log(Status.WARNING, "接口耗时(ms):" + String.valueOf(time));
直接从TestClass 中运行时会报 MyReporter.report
的空指针错误,需做判空处理。
完整代码
package com.ruoyi.listener;import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.ResourceCDN;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.vimalselvam.testng.EmailReporter;
import com.vimalselvam.testng.NodeName;
import com.vimalselvam.testng.SystemInfo;
import com.vimalselvam.testng.listener.ExtentTestNgFormatter;
import org.testng.*;
import org.testng.xml.XmlSuite;import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;public class MyExtentTestNgFormatter extends ExtentTestNgFormatter {private static final String REPORTER_ATTR = "extentTestNgReporter";private static final String SUITE_ATTR = "extentTestNgSuite";private ExtentReports reporter;private List<String> testRunnerOutput;private Map<String, String> systemInfo;private ExtentHtmlReporter htmlReporter;private static ExtentTestNgFormatter instance;public MyExtentTestNgFormatter() {setInstance(this);testRunnerOutput = new ArrayList<>();// reportPath 报告路径String reportPathStr = System.getProperty("reportPath");File reportPath;try {reportPath = new File(reportPathStr);} catch (NullPointerException e) {reportPath = new File(TestNG.DEFAULT_OUTPUTDIR);}if (!reportPath.exists()) {if (!reportPath.mkdirs()) {throw new RuntimeException("Failed to create output run directory");}}// 报告名report.htmlFile reportFile = new File(reportPath, "report.html");// 邮件报告名emailable-report.htmlFile emailReportFile = new File(reportPath, "emailable-report.html");htmlReporter = new ExtentHtmlReporter(reportFile);EmailReporter emailReporter = new EmailReporter(emailReportFile);reporter = new ExtentReports();// 如果cdn.rawgit.com访问不了,可以设置为:ResourceCDN.EXTENTREPORTS或者ResourceCDN.GITHUBhtmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);reporter.attachReporter(htmlReporter, emailReporter);}/*** Gets the instance of the {@link ExtentTestNgFormatter}** @return The instance of the {@link ExtentTestNgFormatter}*/public static ExtentTestNgFormatter getInstance() {return instance;}private static void setInstance(ExtentTestNgFormatter formatter) {instance = formatter;}/*** Gets the system information map** @return The system information map*/public Map<String, String> getSystemInfo() {return systemInfo;}/*** Sets the system information** @param systemInfo The system information map*/public void setSystemInfo(Map<String, String> systemInfo) {this.systemInfo = systemInfo;}public void onStart(ISuite iSuite) {if (iSuite.getXmlSuite().getTests().size() > 0) {ExtentTest suite = reporter.createTest(iSuite.getName());String configFile = iSuite.getParameter("report.config");if (!Strings.isNullOrEmpty(configFile)) {htmlReporter.loadXMLConfig(configFile);}String systemInfoCustomImplName = iSuite.getParameter("system.info");if (!Strings.isNullOrEmpty(systemInfoCustomImplName)) {generateSystemInfo(systemInfoCustomImplName);}iSuite.setAttribute(REPORTER_ATTR, reporter);iSuite.setAttribute(SUITE_ATTR, suite);}}private void generateSystemInfo(String systemInfoCustomImplName) {try {Class<?> systemInfoCustomImplClazz = Class.forName(systemInfoCustomImplName);if (!SystemInfo.class.isAssignableFrom(systemInfoCustomImplClazz)) {throw new IllegalArgumentException("The given system.info class name <" + systemInfoCustomImplName +"> should implement the interface <" + SystemInfo.class.getName() + ">");}SystemInfo t = (SystemInfo) systemInfoCustomImplClazz.newInstance();setSystemInfo(t.getSystemInfo());} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {throw new IllegalStateException(e);}}public void onFinish(ISuite iSuite) {}public void onTestStart(ITestResult iTestResult) {MyReporter.setTestName(iTestResult.getName());}public void onTestSuccess(ITestResult iTestResult) {}public void onTestFailure(ITestResult iTestResult) {}public void onTestSkipped(ITestResult iTestResult) {}public void onTestFailedButWithinSuccessPercentage(ITestResult iTestResult) {}public void onStart(ITestContext iTestContext) {ISuite iSuite = iTestContext.getSuite();ExtentTest suite = (ExtentTest) iSuite.getAttribute(SUITE_ATTR);ExtentTest testContext = suite.createNode(iTestContext.getName());// 自定义报告// 将MyReporter.report 静态引用赋值为 testContext。// testContext 是 @Test每个测试用例时需要的。report.log可以跟随具体的测试用例。另请查阅源码。MyReporter.report = testContext;iTestContext.setAttribute("testContext", testContext);}public void onFinish(ITestContext iTestContext) {ExtentTest testContext = (ExtentTest) iTestContext.getAttribute("testContext");if (iTestContext.getFailedTests().size() > 0) {testContext.fail("Failed");} else if (iTestContext.getSkippedTests().size() > 0) {testContext.skip("Skipped");} else {testContext.pass("Passed");}}public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {if (iInvokedMethod.isTestMethod()) {ITestContext iTestContext = iTestResult.getTestContext();ExtentTest testContext = (ExtentTest) iTestContext.getAttribute("testContext");ExtentTest test = testContext.createNode(iTestResult.getName(), iInvokedMethod.getTestMethod().getDescription());iTestResult.setAttribute("test", test);}}public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {if (iInvokedMethod.isTestMethod()) {ExtentTest test = (ExtentTest) iTestResult.getAttribute("test");List<String> logs = Reporter.getOutput(iTestResult);for (String log : logs) {test.info(log);}int status = iTestResult.getStatus();if (ITestResult.SUCCESS == status) {test.pass("Passed");} else if (ITestResult.FAILURE == status) {test.fail(iTestResult.getThrowable());} else {test.skip("Skipped");}for (String group : iInvokedMethod.getTestMethod().getGroups()) {test.assignCategory(group);}}}/*** Adds a screen shot image file to the report. This method should be used only in the configuration method* and the {@link ITestResult} is the mandatory parameter** @param iTestResult The {@link ITestResult} object* @param filePath The image file path* @throws IOException {@link IOException}*/public void addScreenCaptureFromPath(ITestResult iTestResult, String filePath) throws IOException {ExtentTest test = (ExtentTest) iTestResult.getAttribute("test");test.addScreenCaptureFromPath(filePath);}/*** Adds a screen shot image file to the report. This method should be used only in the* {@link org.testng.annotations.Test} annotated method** @param filePath The image file path* @throws IOException {@link IOException}*/public void addScreenCaptureFromPath(String filePath) throws IOException {ITestResult iTestResult = Reporter.getCurrentTestResult();Preconditions.checkState(iTestResult != null);ExtentTest test = (ExtentTest) iTestResult.getAttribute("test");test.addScreenCaptureFromPath(filePath);}/*** Sets the test runner output** @param message The message to be logged*/public void setTestRunnerOutput(String message) {testRunnerOutput.add(message);}public void generateReport(List<XmlSuite> list, List<ISuite> list1, String s) {if (getSystemInfo() != null) {for (Map.Entry<String, String> entry : getSystemInfo().entrySet()) {reporter.setSystemInfo(entry.getKey(), entry.getValue());}}reporter.setTestRunnerOutput(testRunnerOutput);reporter.flush();}/*** Adds the new node to the test. The node name should have been set already using {@link NodeName}*/public void addNewNodeToTest() {addNewNodeToTest(NodeName.getNodeName());}/*** Adds the new node to the test with the given node name.** @param nodeName The name of the node to be created*/public void addNewNodeToTest(String nodeName) {addNewNode("test", nodeName);}/*** Adds a new node to the suite. The node name should have been set already using {@link NodeName}*/public void addNewNodeToSuite() {addNewNodeToSuite(NodeName.getNodeName());}/*** Adds a new node to the suite with the given node name** @param nodeName The name of the node to be created*/public void addNewNodeToSuite(String nodeName) {addNewNode(SUITE_ATTR, nodeName);}private void addNewNode(String parent, String nodeName) {ITestResult result = Reporter.getCurrentTestResult();Preconditions.checkState(result != null);ExtentTest parentNode = (ExtentTest) result.getAttribute(parent);ExtentTest childNode = parentNode.createNode(nodeName);result.setAttribute(nodeName, childNode);}/*** Adds a info log message to the node. The node name should have been set already using {@link NodeName}** @param logMessage The log message string*/public void addInfoLogToNode(String logMessage) {addInfoLogToNode(logMessage, NodeName.getNodeName());}/*** Adds a info log message to the node** @param logMessage The log message string* @param nodeName The name of the node*/public void addInfoLogToNode(String logMessage, String nodeName) {ITestResult result = Reporter.getCurrentTestResult();Preconditions.checkState(result != null);ExtentTest test = (ExtentTest) result.getAttribute(nodeName);test.info(logMessage);}/*** Marks the node as failed. The node name should have been set already using {@link NodeName}** @param t The {@link Throwable} object*/public void failTheNode(Throwable t) {failTheNode(NodeName.getNodeName(), t);}/*** Marks the given node as failed** @param nodeName The name of the node* @param t The {@link Throwable} object*/public void failTheNode(String nodeName, Throwable t) {ITestResult result = Reporter.getCurrentTestResult();Preconditions.checkState(result != null);ExtentTest test = (ExtentTest) result.getAttribute(nodeName);test.fail(t);}/*** Marks the node as failed. The node name should have been set already using {@link NodeName}** @param logMessage The message to be logged*/public void failTheNode(String logMessage) {failTheNode(NodeName.getNodeName(), logMessage);}/*** Marks the given node as failed** @param nodeName The name of the node* @param logMessage The message to be logged*/public void failTheNode(String nodeName, String logMessage) {ITestResult result = Reporter.getCurrentTestResult();Preconditions.checkState(result != null);ExtentTest test = (ExtentTest) result.getAttribute(nodeName);test.fail(logMessage);}
}class MyReporter {public static ExtentTest report;private static String testName;public static String getTestName() {return testName;}public static void setTestName(String testName) {MyReporter.testName = testName;}
}
Step-3:配置监听
在测试集合 testng.xml 文件中导入 Listener 监听类。
<listeners> <listener class-name="com.zuozewei.extentreportdemo.listener.MyExtentTestNgFormatter"/> </listeners>
Step-4:配置报告
extent reporters
支持报告的配置。目前支持的配置内容有title、主题等。 先在 src/resources/
目录下添加 config/report/extent-config.xml
。
<?xml version="1.0" encoding="UTF-8"?>
<extentreports><configuration><timeStampFormat>yyyy-MM-dd HH:mm:ss</timeStampFormat><!-- report theme --><!-- standard, dark 个人喜好暗色 --><theme>dark</theme><!-- document encoding --><!-- defaults to UTF-8 --><encoding>UTF-8</encoding><!-- protocol for script and stylesheets --><!-- defaults to https --><protocol>https</protocol><!-- title of the document --><documentTitle>接口自动化测试报告</documentTitle><!-- report name - displayed at top-nav --><reportName>接口自动化测试报告</reportName><!-- report headline - displayed at top-nav, after reportHeadline --><reportHeadline>接口自动化测试报告</reportHeadline><!-- global date format override --><!-- defaults to yyyy-MM-dd --><dateFormat>yyyy-MM-dd</dateFormat><!-- global time format override --><!-- defaults to HH:mm:ss --><timeFormat>HH:mm:ss</timeFormat><!-- custom javascript --><scripts><![CDATA[$(document).ready(function() {});]]></scripts><!-- custom styles --><styles><![CDATA[]]></styles></configuration>
</extentreports>
Step-5:配置系统
config下新建 MySystemInfo类继承 SystemInfo 接口
public class MySystemInfo implements SystemInfo {@Overridepublic Map<String, String> getSystemInfo() {Map<String, String> systemInfo = new HashMap<>();systemInfo.put("测试人员", "zuozewei");return systemInfo;}
}
可用于添加系统信息,例如:db的配置信息,人员信息,环境信息等。根据项目实际情况添加。
至此,extentreports 美化报告完成。
Step-6:添加测试用例
public class TestMethodsDemo {@Testpublic void test1(){Assert.assertEquals(1,2);}@Testpublic void test2(){Assert.assertEquals(1,1);}@Testpublic void test3(){Assert.assertEquals("aaa","aaa");}@Testpublic void logDemo(){Reporter.log("这是故意写入的日志");throw new RuntimeException("故意运行时异常");}
}
Step-7:测试用例suite
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" ><suite name="测试demo" verbose="1" preserve-order="true"><parameter name="report.config" value="src/main/resources/report/extent-config.xml"/><parameter name="system.info" value="com.zuozewei.extentreportdemo.config.MySystemInfo"/><test name="测试demo" preserve-order="true"><classes><class name="com.zuozewei.extentreportdemo.testCase.TestMethodsDemo"/></classes></test><listeners><listener class-name="com.zuozewei.extentreportdemo.listener.MyExtentTestNgFormatter"/></listeners>
</suite>
测试报告
HTML Resport 示例
Email Report 示例
工程目录
本文源码:
https://github.com/7DGroup/Java-API-Test-Examples
相关文章:

TestNG与ExtentReport单元测试导出报告文档
TestNG与ExtentReport集成 目录 1 通过实现ITestListener的方法添加Reporter log 1.1 MyTestListener设置 1.2 输出结果 2 TestNG与ExtentReporter集成 2.1 项目结构 2.2 MyExtentReportListener设置 2.3 单多Suite、Test组合测试 2.3.1 单Suite单Test 2.3…...

【JavaEE】_form表单构造HTTP请求
目录 1. form表单的格式 1.1 form表单的常用属性 1.2 form表单的常用搭配标签:input 2. form表单构造GET请求实例 3. form表单构造POST请求实例 4. form表单构造法的缺陷 对于客户端浏览器,以下操作即构造了HTTP请求: 1. 直接在浏览器…...

Mysql中INFORMATION_SCHEMA虚拟库使用
虚拟库字段讲解 #查看INFORMATION_SCHEMA的表信息 DESC information_schema.tables; 重要列: TABLE_SCHEMA #表所在的库 TABLE_NAME #表名 ENGINE #表的存储引擎 TABLE_ROWS #表的行数 DATA_LENGTH #表数据行占用的字节数 AVG_ROW_LENGTH #平均行长度 INDEX_LENGTH…...

【《高性能 MySQL》摘录】第 2 章 MySQL 基准测试
文章目录 2.1 为什么需要基准测试2.2 基准测试的策略2.2.1 测试何种指标 2.3 基准测试方法2.3.1 设计和规划基准测试2.3.2 基准测试应该运行多长时间2.3.3 获取系统性能和状态2.3.4 获得准确的测试结果2.3.5 运行基准测试并分析结果2.3.6 绘图的重要性 2.4 基准测试工具…...

常用的Web应用程序的自动测试工具有哪些
在Web应用程序的自动化测试领域,有许多流行的工具可供选择。以下是一些常用的Web自动化测试工具: 1. Selenium - Selenium是最流行的开源Web应用程序自动化测试套件之一。 - 它支持多种编程语言,如Java、C#、Python、Ruby等。 …...

人工智能与开源机器学习框架
链接:华为机考原题 TensorFlow是一个开源的机器学习框架,由Google开发和维护。它提供了一个针对神经网络和深度学习的强大工具集,能够帮助开发人员构建和训练各种机器学习模型。 TensorFlow的基本概念包括: 张量(Ten…...

高通XBL阶段读取分区
【需求】: 在某些场景下,需要在XBL阶段读取分区数据,需要验证xbl阶段方案 这里主要以裸分区为例,比如oem分区。 1、创建一个1MB大小的oem.img,写入内容“test oem partition” 创建方式: dd if/dev/null …...

[极客大挑战2019]upload
该题考点:后缀黑名单文件内容过滤php木马的几种书写方法 phtml可以解析php代码;<script language"php">eval($_POST[cmd]);</script> 犯蠢的点儿:利用html、php空格和php.不解析<script language"php"&…...

[FastDDS] 基于eProsima FastDDS的移动机器人数据中间件
[FastDDS] 基于eProsima FastDDS的移动机器人数据中间件 注明:无 本栏目主要讲述,基于eProsima FastDDS的移动机器人数据中间件的实现、使用、性能测试。 What is [ FastDDS ]: eProsima Fast DDS是DDS(数据分发服务)规范的C实现…...

实现外网手机或者电脑随时随地远程访问家里的电脑主机(linux为例)
文章目录 一、背景概要二、安装配置花生壳软件(linux版本)三、手机端(外网)验证连接四、安装ubuntu20server版系统遇到的问题记录 一、背景概要 由于经常在遇到某些问题的时候,针对某一个场景的理解,需要借助于自己的电脑去编译(aosp/linux/qemu)代码查…...

spring boot集成redis
引入依赖 <!-- redis依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- 连接池依赖 --><dependency><groupId>org.ap…...

Docker的常用命令
Docker的常用命令 Docker是一个开源的应用容器引擎,它使得开发者能够打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间没有任何接口ÿ…...

JSON简介与基本使用
JSON简介与基本使用 引言 在现今的互联网开发中,数据交换格式的选择至关重要。其中,JSON(JavaScript Object Notation)作为一种轻量级的数据交换格式,因其简洁、易读和易写的特性而备受青睐。本文将简要介绍JSON的基…...

好物周刊#40:多功能文件管理器
https://github.com/cunyu1943/JavaPark https://yuque.com/cunyu1943 村雨遥的好物周刊,记录每周看到的有价值的信息,主要针对计算机领域,每周五发布。 一、项目 1. 中国节假日补班日历 中国节假日、调休、补班日历,ICS 格式…...

【洛谷 P8780】[蓝桥杯 2022 省 B] 刷题统计 题解(贪心算法+模拟+四则运算)
[蓝桥杯 2022 省 B] 刷题统计 题目描述 小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天做 a a a 道题目,周六和周日每天做 b b b 道题目。请你帮小明计算,按照计划他将在第几天实现做题数大于等于 n n n 题? 输入格式 输入一…...

【蓝桥杯入门记录】静态数码管例程
目录 一、补充 (code) 二、例程 (1)例程1:数码管显示某一位(某一杠)。以点亮8段数码管最上面的横杠为例。 (2)例程2:数码管的8个段依次点亮(其他…...

6.openEuler系统服务的配置和管理(二)
openEuler OECA认证辅导,标红的文字为学习重点和考点。 如果需要做实验,建议安装麒麟信安、银河麒麟、统信等具有图形化的操作系统,其安装与openeuler基本一致。 3.任务管理 任务的概念和相关术语: 当你在终端或控制台工作时,可能不希望由于运行一个作业而占住了屏幕,因…...

一招鲜吃遍天!ChatGPT高级咒语揭秘:记忆、洗稿、速写SEO文章(一)
🌟 摘要 🌟 这个专栏系列的初衷是针对特定痛点精心设计GPT提示词,在这篇文章中,我们深入探讨了利用GPT技术解决三个常见挑战:增强记忆力、内容创新、以及SEO文章速写的高级技巧。这些挑战分别对应三个独特的解决策略,我们将逐一详细解析。 首先,解决记忆增强的挑战,我…...

LeetCode 每日一题 2024/2/19-2024/2/25
记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步 目录 2/19 590. N 叉树的后序遍历2/20 105. 从前序与中序遍历序列构造二叉树2/21 106. 从中序与后序遍历序列构造二叉树2/22 889. 根据前序和后序遍历构造二叉树2/23 2583. 二叉…...

Javaweb之SpringBootWeb案例之配置优先级的详细解析
1. 配置优先级 在我们前面的课程当中,我们已经讲解了SpringBoot项目当中支持的三类配置文件: application.properties application.yml application.yaml 在SpringBoot项目当中,我们要想配置一个属性,可以通过这三种方式当中…...

GO框架基础 (三)、xorm库
xorm介绍 官网:https://xorm.io/ git文档:https://github.com/go-xorm/xorm xorm 是一个 Go 语言的 ORM(对象关系映射)库,它提供了一种简单、高效的方式来将 Go 语言中的结构体与数据库表进行映射,并提供了…...

神经网络系列---回归问题和分类问题
文章目录 回归问题和分类问题回归问题:分类问题:多分类问题:排序问题:自定义损失函数: 回归问题和分类问题 回归问题: 回归问题是一种预测连续数值输出的任务。在这种问题中,模型的目标是根据…...

Jetpack Compose 与 Kotlin 的兼容性对应关系
点击查看:Jetpack Compose 教程 点击查看:Jetpack Compose Kotlin 的兼容性 官网 声明依赖项 如需添加 Compose Compiler 的依赖项,您必须将 Google Maven 代码库添加到项目中。如需了解详情,请参阅 Google 的 Maven 代码库。 …...

汇编反外挂
在软件保护领域,尤其是游戏保护中,反外挂是一个重要的议题。外挂通常指的是一种第三方软件,它可以修改游戏数据、操作游戏内存或提供其他作弊功能,从而给玩家带来不公平的优势。为了打击外挂,游戏开发者会采取一系列措…...

134 Linux 系统编程11 ,readlink命令,文件目录rwx权限差异,目录操作函数
一 readlink 命令 前面知道,如果a.soft是一个软链接,我们使用 cat a.soft,会直接查看这个软链接指向的文件 那么我们就是想看这个软链接是啥,可以使用 readlink a.soft 二 获取工作目录 getcwd函数 获取进程当前工作目录 (卷3,标…...

仿12306校招项目业务二(列车检索)
目录 验证数据 加载城市数据 查询列车站点信息 查询列车余票信息 构建列车返回数据 12306 项目中列车数据检索接口路径  TicketController的pageListTicketQuery。 GetMapping("/api/ticket-service/ticket/query")public Result<T…...

前端架构: 实现脚手架终端UI样式之ANSI escape code, Chalk, Ora介绍
在脚手架当中实现命令行的UI显示 1 )概述 在命令行中,如果想实现除传统的常规文本以外的内容比如想对字体进行加粗斜体下划线,包括对它改变颜色改变前景色改变后景色等等需要借助一个叫做 ANSI escape code 这样的一个概念它其实是一个标准&…...

platform(驱动层+应用层)实现终端和中断开关点灯
设备树文件添加 myplatform{compatible"hqyj,myplatform";interrupt-parent<&gpiof>;interrupts<8 0>,<7 0>,<9 0>;led1-gpio<&gpioe 10 0>;led2-gpio<&gpiof 10 0>;led3-gpio<&gpioe 8 0>;reg<0x123…...

黑马JavaWeb开发跟学(一)Web前端开发HTML、CSS基础
黑马JavaWeb开发一.Web前端开发HTML、CSS基础 引子、Web开发介绍传统路线本课程全新路线本课程适用人群课程收获一、什么是web开发二、网站的工作流程三、网站的开发模式四、网站的开发技术 前端开发基础一、前端开发二、HTML & CSS2.1 HTML快速入门2.1.1 操作第一步第二步…...

Nest.js权限管理系统开发(四)Swagger API接入
什么是swagger Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(<https://swagger.io/>)。 它的主要作用是: 1. 使得前后端分离开发更加方便,有利于团队协作 2. 接口的文档在线自动生成…...