赞
踩
目录
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
TestNG的Listener列表
TestNG提供了一组预定义的Listener Java接口,这些接口全部继承自TestNG的 ITestNGListener接口。用户创建这些接口的实现类,并把它们加入到 TestNG 中,TestNG便会在测试运行的不同时刻调用这些类中的接口方法:
本文将着重介绍最常用到的两个Listener ITestListener与Ireporter接口。
通过实现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; } }
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 项目结构
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>
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组合代替报告中的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(); } }
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
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分组
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注解
引言
在走进Java接口测试之测试框架TestNG 中我们详细介绍了 TestNG 的各种用法, 在本文中,我将详细介绍如何将 ExtentReports 测试报告与TestNG集成。
主要特点:
TestNG 原生报告有点丑,信息整理有点乱。ExtentReports 是用于替换TestNG 原生报告。当然也可以使用 ReportNg,个人偏好 ExtentReports 样式。
官网已经给了很多demo了,大家可以参考练习。
官网:http://extentreports.com/
客户端:
https://github.com/anshooarora/extentreports-java/commits/master
服务端:https://github.com/anshooarora/extentx
引入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>
主要基于以下两项原因:
下载 ExtentReportes 源码,找到 ExtentTestNgFormatter 类,Listener 目录下创建 MyExtentTestNgFormatter.java
类直接继承 ExtentTestNgFormatter 类。
public class MyExtentTestNgFormatter extends ExtentTestNgFormatter {
构造方法加入
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.GITHUB htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS); reporter.attachReporter(htmlReporter, emailReporter); }
新建一个类名为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.html File reportFile = new File(reportPath, "report.html"); // 邮件报告名emailable-report.html File 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 支持多种玩法
// 根据状态不同添加报告。型如警告 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.html File reportFile = new File(reportPath, "report.html"); // 邮件报告名emailable-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.GITHUB htmlReporter.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; } }
在测试集合 testng.xml 文件中导入 Listener 监听类。
<listeners>
<listener class-name="com.zuozewei.extentreportdemo.listener.MyExtentTestNgFormatter"/>
</listeners>
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>
config下新建 MySystemInfo类继承 SystemInfo 接口
public class MySystemInfo implements SystemInfo {
@Override
public Map<String, String> getSystemInfo() {
Map<String, String> systemInfo = new HashMap<>();
systemInfo.put("测试人员", "zuozewei");
return systemInfo;
}
}
可用于添加系统信息,例如:db的配置信息,人员信息,环境信息等。根据项目实际情况添加。
至此,extentreports 美化报告完成。
public class TestMethodsDemo { @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("故意运行时异常"); } }
<!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>
本文源码:
https://github.com/7DGroup/Java-API-Test-Examples
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。