依赖导入

  1. <!-- https://mvnrepository.com/artifact/com.aventstack/extentreports -->
  2. <dependency>
  3. <groupId>com.aventstack</groupId>
  4. <artifactId>extentreports</artifactId>
  5. <version>3.1.5</version>
  6. <scope>provided</scope>
  7. </dependency>
  8. <!-- https://mvnrepository.com/artifact/com.vimalselvam/testng-extentsreport -->
  9. <dependency>
  10. <groupId>com.vimalselvam</groupId>
  11. <artifactId>testng-extentsreport</artifactId>
  12. <version>1.3.1</version>
  13. </dependency>

监听类实现

创建main/java/reporter

方案一:自行实现IReporter 接口,实现监听器

  1. import com.aventstack.extentreports.ExtentReports;
  2. import com.aventstack.extentreports.ExtentTest;
  3. import com.aventstack.extentreports.ResourceCDN;
  4. import com.aventstack.extentreports.Status;
  5. import com.aventstack.extentreports.model.TestAttribute;
  6. import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
  7. import com.aventstack.extentreports.reporter.configuration.ChartLocation;
  8. import com.aventstack.extentreports.reporter.configuration.Theme;
  9. import org.testng.*;
  10. import org.testng.xml.XmlSuite;
  11. import java.io.File;
  12. import java.util.*;
  13. public class ExtentTestNGIReporterListener implements IReporter {
  14. //生成的路径以及文件名
  15. private static final String OUTPUT_FOLDER = "test-output/";
  16. private static final String FILE_NAME = "index.html";
  17. private ExtentReports extent;
  18. @Override
  19. public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
  20. init();
  21. boolean createSuiteNode = false;
  22. if(suites.size()>1){
  23. createSuiteNode=true;
  24. }
  25. for (ISuite suite : suites) {
  26. Map<String, ISuiteResult> result = suite.getResults();
  27. //如果suite里面没有任何用例,直接跳过,不在报告里生成
  28. if(result.size()==0){
  29. continue;
  30. }
  31. //统计suite下的成功、失败、跳过的总用例数
  32. int suiteFailSize=0;
  33. int suitePassSize=0;
  34. int suiteSkipSize=0;
  35. ExtentTest suiteTest=null;
  36. //存在多个suite的情况下,在报告中将同一个一个suite的测试结果归为一类,创建一级节点。
  37. if(createSuiteNode){
  38. suiteTest = extent.createTest(suite.getName()).assignCategory(suite.getName());
  39. }
  40. boolean createSuiteResultNode = false;
  41. if(result.size()>1){
  42. createSuiteResultNode=true;
  43. }
  44. for (ISuiteResult r : result.values()) {
  45. ExtentTest resultNode;
  46. ITestContext context = r.getTestContext();
  47. if(createSuiteResultNode){
  48. //没有创建suite的情况下,将在SuiteResult的创建为一级节点,否则创建为suite的一个子节点。
  49. if( null == suiteTest){
  50. resultNode = extent.createTest(r.getTestContext().getName());
  51. }else{
  52. resultNode = suiteTest.createNode(r.getTestContext().getName());
  53. }
  54. }else{
  55. resultNode = suiteTest;
  56. }
  57. if(resultNode != null){
  58. resultNode.getModel().setName(suite.getName()+" : "+r.getTestContext().getName());
  59. if(resultNode.getModel().hasCategory()){
  60. resultNode.assignCategory(r.getTestContext().getName());
  61. }else{
  62. resultNode.assignCategory(suite.getName(),r.getTestContext().getName());
  63. }
  64. resultNode.getModel().setStartTime(r.getTestContext().getStartDate());
  65. resultNode.getModel().setEndTime(r.getTestContext().getEndDate());
  66. //统计SuiteResult下的数据
  67. int passSize = r.getTestContext().getPassedTests().size();
  68. int failSize = r.getTestContext().getFailedTests().size();
  69. int skipSize = r.getTestContext().getSkippedTests().size();
  70. suitePassSize += passSize;
  71. suiteFailSize += failSize;
  72. suiteSkipSize += skipSize;
  73. if(failSize>0){
  74. resultNode.getModel().setStatus(Status.FAIL);
  75. }
  76. resultNode.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",passSize,failSize,skipSize));
  77. }
  78. buildTestNodes(resultNode,context.getFailedTests(), Status.FAIL);
  79. buildTestNodes(resultNode,context.getSkippedTests(), Status.SKIP);
  80. buildTestNodes(resultNode,context.getPassedTests(), Status.PASS);
  81. }
  82. if(suiteTest!= null){
  83. suiteTest.getModel().setDescription(String.format("Pass: %s ; Fail: %s ; Skip: %s ;",suitePassSize,suiteFailSize,suiteSkipSize));
  84. if(suiteFailSize>0){
  85. suiteTest.getModel().setStatus(Status.FAIL);
  86. }
  87. }
  88. }
  89. // for (String s : Reporter.getOutput()) {
  90. // extent.setTestRunnerOutput(s);
  91. // }
  92. extent.flush();
  93. }
  94. private void init() {
  95. //文件夹不存在的话进行创建
  96. File reportDir= new File(OUTPUT_FOLDER);
  97. if(!reportDir.exists()&& !reportDir .isDirectory()){
  98. reportDir.mkdir();
  99. }
  100. ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);
  101. // 设置静态文件的DNS
  102. //怎么样解决cdn.rawgit.com访问不了的情况
  103. htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);
  104. htmlReporter.config().setDocumentTitle("api自动化测试报告");
  105. htmlReporter.config().setReportName("api自动化测试报告");
  106. htmlReporter.config().setChartVisibilityOnOpen(true);
  107. htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
  108. htmlReporter.config().setTheme(Theme.STANDARD);
  109. htmlReporter.config().setCSS(".node.level-1 ul{ display:none;} .node.level-1.active ul{display:block;}");
  110. extent = new ExtentReports();
  111. extent.attachReporter(htmlReporter);
  112. extent.setReportUsesManualConfiguration(true);
  113. }
  114. private void buildTestNodes(ExtentTest extenttest, IResultMap tests, Status status) {
  115. //存在父节点时,获取父节点的标签
  116. String[] categories=new String[0];
  117. if(extenttest != null ){
  118. List<TestAttribute> categoryList = extenttest.getModel().getCategoryContext().getAll();
  119. categories = new String[categoryList.size()];
  120. for(int index=0;index<categoryList.size();index++){
  121. categories[index] = categoryList.get(index).getName();
  122. }
  123. }
  124. ExtentTest test;
  125. if (tests.size() > 0) {
  126. //调整用例排序,按时间排序
  127. Set<ITestResult> treeSet = new TreeSet<ITestResult>(new Comparator<ITestResult>() {
  128. @Override
  129. public int compare(ITestResult o1, ITestResult o2) {
  130. return o1.getStartMillis()<o2.getStartMillis()?-1:1;
  131. }
  132. });
  133. treeSet.addAll(tests.getAllResults());
  134. for (ITestResult result : treeSet) {
  135. Object[] parameters = result.getParameters();
  136. String name="";
  137. //如果有参数,则使用参数的toString组合代替报告中的name
  138. for(Object param:parameters){
  139. name+=param.toString();
  140. }
  141. if(name.length()>0){
  142. if(name.length()>50){
  143. name= name.substring(0,49)+"...";
  144. }
  145. }else{
  146. name = result.getMethod().getMethodName();
  147. }
  148. if(extenttest==null){
  149. test = extent.createTest(name);
  150. }else{
  151. //作为子节点进行创建时,设置同父节点的标签一致,便于报告检索。
  152. test = extenttest.createNode(name).assignCategory(categories);
  153. }
  154. //test.getModel().setDescription(description.toString());
  155. //test = extent.createTest(result.getMethod().getMethodName());
  156. for (String group : result.getMethod().getGroups())
  157. test.assignCategory(group);
  158. List<String> outputList = Reporter.getOutput(result);
  159. for(String output:outputList){
  160. //将用例的log输出报告中
  161. test.debug(output);
  162. }
  163. if (result.getThrowable() != null) {
  164. test.log(status, result.getThrowable());
  165. }
  166. else {
  167. test.log(status, "Test " + status.toString().toLowerCase() + "ed");
  168. }
  169. test.getModel().setStartTime(getTime(result.getStartMillis()));
  170. test.getModel().setEndTime(getTime(result.getEndMillis()));
  171. }
  172. }
  173. }
  174. private Date getTime(long millis) {
  175. Calendar calendar = Calendar.getInstance();
  176. calendar.setTimeInMillis(millis);
  177. return calendar.getTime();
  178. }
  179. }

方案二:强制重写ExtentTestNgFormatter类,实现监听器

  1. import com.aventstack.extentreports.ExtentReports;
  2. import com.aventstack.extentreports.ExtentTest;
  3. import com.aventstack.extentreports.ResourceCDN;
  4. import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
  5. import com.google.common.base.Preconditions;
  6. import com.google.common.base.Strings;
  7. import com.vimalselvam.testng.EmailReporter;
  8. import com.vimalselvam.testng.NodeName;
  9. import com.vimalselvam.testng.SystemInfo;
  10. import com.vimalselvam.testng.listener.ExtentTestNgFormatter;
  11. import org.testng.*;
  12. import org.testng.xml.XmlSuite;
  13. import java.io.File;
  14. import java.io.IOException;
  15. import java.util.ArrayList;
  16. import java.util.List;
  17. import java.util.Map;
  18. public class MyExtentTestNgFormatter extends ExtentTestNgFormatter {
  19. private static final String REPORTER_ATTR = "extentTestNgReporter";
  20. private static final String SUITE_ATTR = "extentTestNgSuite";
  21. private ExtentReports reporter;
  22. private List<String> testRunnerOutput;
  23. private Map<String, String> systemInfo;
  24. private ExtentHtmlReporter htmlReporter;
  25. private static ExtentTestNgFormatter instance;
  26. public MyExtentTestNgFormatter() {
  27. setInstance(this);
  28. testRunnerOutput = new ArrayList<>();
  29. String reportPathStr = System.getProperty("reportPath");
  30. File reportPath;
  31. try {
  32. reportPath = new File(reportPathStr);
  33. } catch (NullPointerException e) {
  34. reportPath = new File(TestNG.DEFAULT_OUTPUTDIR);
  35. }
  36. if (!reportPath.exists()) {
  37. if (!reportPath.mkdirs()) {
  38. throw new RuntimeException("Failed to create output run directory");
  39. }
  40. }
  41. File reportFile = new File(reportPath, "report.html");
  42. File emailReportFile = new File(reportPath, "emailable-report.html");
  43. htmlReporter = new ExtentHtmlReporter(reportFile);
  44. EmailReporter emailReporter = new EmailReporter(emailReportFile);
  45. reporter = new ExtentReports();
  46. // 如果cdn.rawgit.com访问不了,可以设置为:ResourceCDN.EXTENTREPORTS或者ResourceCDN.GITHUB
  47. htmlReporter.config().setResourceCDN(ResourceCDN.EXTENTREPORTS);
  48. reporter.attachReporter(htmlReporter, emailReporter);
  49. }
  50. /**
  51. * Gets the instance of the {@link ExtentTestNgFormatter}
  52. *
  53. * @return The instance of the {@link ExtentTestNgFormatter}
  54. */
  55. public static ExtentTestNgFormatter getInstance() {
  56. return instance;
  57. }
  58. private static void setInstance(ExtentTestNgFormatter formatter) {
  59. instance = formatter;
  60. }
  61. /**
  62. * Gets the system information map
  63. *
  64. * @return The system information map
  65. */
  66. public Map<String, String> getSystemInfo() {
  67. return systemInfo;
  68. }
  69. /**
  70. * Sets the system information
  71. *
  72. * @param systemInfo The system information map
  73. */
  74. public void setSystemInfo(Map<String, String> systemInfo) {
  75. this.systemInfo = systemInfo;
  76. }
  77. public void onStart(ISuite iSuite) {
  78. if (iSuite.getXmlSuite().getTests().size() > 0) {
  79. ExtentTest suite = reporter.createTest(iSuite.getName());
  80. String configFile = iSuite.getParameter("report.config");
  81. if (!Strings.isNullOrEmpty(configFile)) {
  82. htmlReporter.loadXMLConfig(configFile);
  83. }
  84. String systemInfoCustomImplName = iSuite.getParameter("system.info");
  85. if (!Strings.isNullOrEmpty(systemInfoCustomImplName)) {
  86. generateSystemInfo(systemInfoCustomImplName);
  87. }
  88. iSuite.setAttribute(REPORTER_ATTR, reporter);
  89. iSuite.setAttribute(SUITE_ATTR, suite);
  90. }
  91. }
  92. private void generateSystemInfo(String systemInfoCustomImplName) {
  93. try {
  94. Class<?> systemInfoCustomImplClazz = Class.forName(systemInfoCustomImplName);
  95. if (!SystemInfo.class.isAssignableFrom(systemInfoCustomImplClazz)) {
  96. throw new IllegalArgumentException("The given system.info class name <" + systemInfoCustomImplName +
  97. "> should implement the interface <" + SystemInfo.class.getName() + ">");
  98. }
  99. SystemInfo t = (SystemInfo) systemInfoCustomImplClazz.newInstance();
  100. setSystemInfo(t.getSystemInfo());
  101. } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
  102. throw new IllegalStateException(e);
  103. }
  104. }
  105. public void onFinish(ISuite iSuite) {
  106. }
  107. public void onTestStart(ITestResult iTestResult) {
  108. MyReporter.setTestName(iTestResult.getName());
  109. }
  110. public void onTestSuccess(ITestResult iTestResult) {
  111. }
  112. public void onTestFailure(ITestResult iTestResult) {
  113. }
  114. public void onTestSkipped(ITestResult iTestResult) {
  115. }
  116. public void onTestFailedButWithinSuccessPercentage(ITestResult iTestResult) {
  117. }
  118. public void onStart(ITestContext iTestContext) {
  119. ISuite iSuite = iTestContext.getSuite();
  120. ExtentTest suite = (ExtentTest) iSuite.getAttribute(SUITE_ATTR);
  121. ExtentTest testContext = suite.createNode(iTestContext.getName());
  122. // 自定义报告
  123. // 将MyReporter.report静态引用赋值为testContext。
  124. // testContext是@Test每个测试用例时需要的。report.log可以跟随具体的测试用例。另请查阅源码。
  125. MyReporter.report = testContext;
  126. iTestContext.setAttribute("testContext", testContext);
  127. }
  128. public void onFinish(ITestContext iTestContext) {
  129. ExtentTest testContext = (ExtentTest) iTestContext.getAttribute("testContext");
  130. if (iTestContext.getFailedTests().size() > 0) {
  131. testContext.fail("Failed");
  132. } else if (iTestContext.getSkippedTests().size() > 0) {
  133. testContext.skip("Skipped");
  134. } else {
  135. testContext.pass("Passed");
  136. }
  137. }
  138. public void beforeInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
  139. if (iInvokedMethod.isTestMethod()) {
  140. ITestContext iTestContext = iTestResult.getTestContext();
  141. ExtentTest testContext = (ExtentTest) iTestContext.getAttribute("testContext");
  142. ExtentTest test = testContext.createNode(iTestResult.getName(), iInvokedMethod.getTestMethod().getDescription());
  143. iTestResult.setAttribute("test", test);
  144. }
  145. }
  146. public void afterInvocation(IInvokedMethod iInvokedMethod, ITestResult iTestResult) {
  147. if (iInvokedMethod.isTestMethod()) {
  148. ExtentTest test = (ExtentTest) iTestResult.getAttribute("test");
  149. List<String> logs = Reporter.getOutput(iTestResult);
  150. for (String log : logs) {
  151. test.info(log);
  152. }
  153. int status = iTestResult.getStatus();
  154. if (ITestResult.SUCCESS == status) {
  155. test.pass("Passed");
  156. } else if (ITestResult.FAILURE == status) {
  157. test.fail(iTestResult.getThrowable());
  158. } else {
  159. test.skip("Skipped");
  160. }
  161. for (String group : iInvokedMethod.getTestMethod().getGroups()) {
  162. test.assignCategory(group);
  163. }
  164. }
  165. }
  166. /**
  167. * Adds a screen shot image file to the report. This method should be used only in the configuration method
  168. * and the {@link ITestResult} is the mandatory parameter
  169. *
  170. * @param iTestResult The {@link ITestResult} object
  171. * @param filePath The image file path
  172. * @throws IOException {@link IOException}
  173. */
  174. public void addScreenCaptureFromPath(ITestResult iTestResult, String filePath) throws IOException {
  175. ExtentTest test = (ExtentTest) iTestResult.getAttribute("test");
  176. test.addScreenCaptureFromPath(filePath);
  177. }
  178. /**
  179. * Adds a screen shot image file to the report. This method should be used only in the
  180. * {@link org.testng.annotations.Test} annotated method
  181. *
  182. * @param filePath The image file path
  183. * @throws IOException {@link IOException}
  184. */
  185. public void addScreenCaptureFromPath(String filePath) throws IOException {
  186. ITestResult iTestResult = Reporter.getCurrentTestResult();
  187. Preconditions.checkState(iTestResult != null);
  188. ExtentTest test = (ExtentTest) iTestResult.getAttribute("test");
  189. test.addScreenCaptureFromPath(filePath);
  190. }
  191. /**
  192. * Sets the test runner output
  193. *
  194. * @param message The message to be logged
  195. */
  196. public void setTestRunnerOutput(String message) {
  197. testRunnerOutput.add(message);
  198. }
  199. public void generateReport(List<XmlSuite> list, List<ISuite> list1, String s) {
  200. if (getSystemInfo() != null) {
  201. for (Map.Entry<String, String> entry : getSystemInfo().entrySet()) {
  202. reporter.setSystemInfo(entry.getKey(), entry.getValue());
  203. }
  204. }
  205. reporter.setTestRunnerOutput(testRunnerOutput);
  206. reporter.flush();
  207. }
  208. /**
  209. * Adds the new node to the test. The node name should have been set already using {@link NodeName}
  210. */
  211. public void addNewNodeToTest() {
  212. addNewNodeToTest(NodeName.getNodeName());
  213. }
  214. /**
  215. * Adds the new node to the test with the given node name.
  216. *
  217. * @param nodeName The name of the node to be created
  218. */
  219. public void addNewNodeToTest(String nodeName) {
  220. addNewNode("test", nodeName);
  221. }
  222. /**
  223. * Adds a new node to the suite. The node name should have been set already using {@link NodeName}
  224. */
  225. public void addNewNodeToSuite() {
  226. addNewNodeToSuite(NodeName.getNodeName());
  227. }
  228. /**
  229. * Adds a new node to the suite with the given node name
  230. *
  231. * @param nodeName The name of the node to be created
  232. */
  233. public void addNewNodeToSuite(String nodeName) {
  234. addNewNode(SUITE_ATTR, nodeName);
  235. }
  236. private void addNewNode(String parent, String nodeName) {
  237. ITestResult result = Reporter.getCurrentTestResult();
  238. Preconditions.checkState(result != null);
  239. ExtentTest parentNode = (ExtentTest) result.getAttribute(parent);
  240. ExtentTest childNode = parentNode.createNode(nodeName);
  241. result.setAttribute(nodeName, childNode);
  242. }
  243. /**
  244. * Adds a info log message to the node. The node name should have been set already using {@link NodeName}
  245. *
  246. * @param logMessage The log message string
  247. */
  248. public void addInfoLogToNode(String logMessage) {
  249. addInfoLogToNode(logMessage, NodeName.getNodeName());
  250. }
  251. /**
  252. * Adds a info log message to the node
  253. *
  254. * @param logMessage The log message string
  255. * @param nodeName The name of the node
  256. */
  257. public void addInfoLogToNode(String logMessage, String nodeName) {
  258. ITestResult result = Reporter.getCurrentTestResult();
  259. Preconditions.checkState(result != null);
  260. ExtentTest test = (ExtentTest) result.getAttribute(nodeName);
  261. test.info(logMessage);
  262. }
  263. /**
  264. * Marks the node as failed. The node name should have been set already using {@link NodeName}
  265. *
  266. * @param t The {@link Throwable} object
  267. */
  268. public void failTheNode(Throwable t) {
  269. failTheNode(NodeName.getNodeName(), t);
  270. }
  271. /**
  272. * Marks the given node as failed
  273. *
  274. * @param nodeName The name of the node
  275. * @param t The {@link Throwable} object
  276. */
  277. public void failTheNode(String nodeName, Throwable t) {
  278. ITestResult result = Reporter.getCurrentTestResult();
  279. Preconditions.checkState(result != null);
  280. ExtentTest test = (ExtentTest) result.getAttribute(nodeName);
  281. test.fail(t);
  282. }
  283. /**
  284. * Marks the node as failed. The node name should have been set already using {@link NodeName}
  285. *
  286. * @param logMessage The message to be logged
  287. */
  288. public void failTheNode(String logMessage) {
  289. failTheNode(NodeName.getNodeName(), logMessage);
  290. }
  291. /**
  292. * Marks the given node as failed
  293. *
  294. * @param nodeName The name of the node
  295. * @param logMessage The message to be logged
  296. */
  297. public void failTheNode(String nodeName, String logMessage) {
  298. ITestResult result = Reporter.getCurrentTestResult();
  299. Preconditions.checkState(result != null);
  300. ExtentTest test = (ExtentTest) result.getAttribute(nodeName);
  301. test.fail(logMessage);
  302. }
  303. }

创建MyReporter.java,用于静态ExtentTest的引用

  1. public class MyReporter {
  2. public static ExtentTest report;
  3. }

可以通过report.log实现一些内容,例如实现一个拦截器:

  1. import com.aventstack.extentreports.Status;
  2. import okhttp3.Interceptor;
  3. import okhttp3.Request;
  4. import okhttp3.Response;
  5. import reporter.Listener.MyReporter;
  6. import java.io.IOException;
  7. /**
  8. * 自定义拦截器--超时拦截器
  9. * 验证响应时间超过100毫秒,则警告。
  10. * 也可以自定义添加拦截器
  11. *
  12. * @author jx
  13. * @Date: 2018/7/20 09:44
  14. */
  15. public class MyInterceptor implements Interceptor {
  16. @Override
  17. public Response intercept(Chain chain) throws IOException {
  18. Request request = chain.request();
  19. Response response = chain.proceed(request);
  20. long time = response.receivedResponseAtMillis() - response.sentRequestAtMillis();
  21. if (time > 100) {
  22. MyReporter.report.log(Status.WARNING, MyReporter.getTestName() + " 接口耗时:" + time);
  23. }
  24. return response;
  25. }
  26. }

配置导入监听类

在测试集合.xml文件中导入Listener监听类

  1. <listeners>
  2. <listener class-name="reporter.ExtentTestNGIReporterListener"/>
  3. </listeners>

报告配置

在src/resources/目录下添加 config/report/extent-config.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <extentreports>
  3. <configuration>
  4. <timeStampFormat>yyyy-MM-dd HH:mm:ss</timeStampFormat>
  5. <!-- report theme -->
  6. <!-- standard, dark 个人喜好暗色 -->
  7. <theme>dark</theme>
  8. <!-- document encoding -->
  9. <!-- defaults to UTF-8 -->
  10. <encoding>UTF-8</encoding>
  11. <!-- protocol for script and stylesheets -->
  12. <!-- defaults to https -->
  13. <protocol>https</protocol>
  14. <!-- title of the document -->
  15. <documentTitle>QA-接口自动化测试报告</documentTitle>
  16. <!-- report name - displayed at top-nav -->
  17. <reportName>QA-接口自动化测试报告</reportName>
  18. <!-- report headline - displayed at top-nav, after reportHeadline -->
  19. <reportHeadline>接口自动化测试报告</reportHeadline>
  20. <!-- global date format override -->
  21. <!-- defaults to yyyy-MM-dd -->
  22. <dateFormat>yyyy-MM-dd</dateFormat>
  23. <!-- global time format override -->
  24. <!-- defaults to HH:mm:ss -->
  25. <timeFormat>HH:mm:ss</timeFormat>
  26. <!-- custom javascript -->
  27. <scripts>
  28. <![CDATA[
  29. $(document).ready(function() {
  30. });
  31. ]]>
  32. </scripts>
  33. <!-- custom styles -->
  34. <styles>
  35. <![CDATA[
  36. ]]>
  37. </styles>
  38. </configuration>
  39. </extentreports>

在src/main/java/reporter/config目录下创建MySystemInfo.java类,继承SystemInfo接口

  1. public class MySystemInfo implements SystemInfo {
  2. @Override
  3. public Map<String, String> getSystemInfo() {
  4. InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("env.properties");
  5. Properties properties = new Properties();
  6. Map<String, String> systemInfo = new HashMap<>();
  7. try {
  8. properties.load(inputStream);
  9. systemInfo.put("environment", properties.getProperty("Environment"));
  10. systemInfo.put("sqlURL", properties.getProperty("ESsql.URL"));
  11. systemInfo.put("redisHost", properties.getProperty("redis.host"));
  12. systemInfo.put("redisPort", properties.getProperty("redis.port"));
  13. systemInfo.put("mongodbHost", properties.getProperty("mongodb.host"));
  14. systemInfo.put("mongodbPort", properties.getProperty("mongodb.port"));
  15. systemInfo.put("测试人员", "jxq");
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. return systemInfo;
  20. }
  21. }

在测试集合.xml文件中导入

  1. <!-- 自定义参数,用于报告展示-->
  2. <parameter name="report.config" value="src/main/resources/config/report/extent-config.xml"/>
  3. <parameter name="system.info" value="reporter.config.MySystemInfo"/>