如何让TestNG的Reporter.log()消息显示在HTML报告的Results区域?
实现方法
要让Reporter.log()的消息和对应测试方法绑定,在点击Results区域的测试方法时展开展示,核心是要把每个测试方法的日志和方法本身关联起来,再通过自定义报告逻辑实现点击切换效果,具体步骤如下:
步骤1:自定义监听器收集测试方法日志
实现TestNG的ITestListener接口,在测试方法生命周期节点中捕获Reporter.log()的输出,并和当前测试方法绑定存储:import org.testng.ITestListener; import org.testng.ITestResult; import org.testng.Reporter; import java.util.HashMap; import java.util.List; import java.util.Map; public class TestLogCollector implements ITestListener { private final Map<String, List<String>> methodLogsMap = new HashMap<>(); @Override public void onTestStart(ITestResult result) { // 测试方法开始前清空日志缓存 Reporter.clear(); } @Override public void onTestSuccess(ITestResult result) { saveMethodLogs(result); } @Override public void onTestFailure(ITestResult result) { saveMethodLogs(result); } @Override public void onTestSkipped(ITestResult result) { saveMethodLogs(result); } private void saveMethodLogs(ITestResult result) { String methodFullName = result.getMethod().getQualifiedName(); List<String> currentLogs = Reporter.getOutput(result); methodLogsMap.put(methodFullName, currentLogs); } // 提供日志获取方法,供报告生成器使用 public Map<String, List<String>> getMethodLogs() { return methodLogsMap; } }步骤2:自定义HTML报告生成器
实现TestNG的IReporter接口,基于收集到的日志数据,生成带点击展开功能的HTML报告,将日志嵌入对应测试方法的结果区域:import org.testng.IReporter; import org.testng.ISuite; import org.testng.ISuiteResult; import org.testng.ITestResult; import org.testng.xml.XmlSuite; import java.io.FileWriter; import java.io.IOException; import java.util.List; import java.util.Map; public class CustomTestReporter implements IReporter { private final TestLogCollector logCollector; public CustomTestReporter(TestLogCollector collector) { this.logCollector = collector; } @Override public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDir) { try (FileWriter writer = new FileWriter(outputDir + "/custom-test-report.html")) { writer.write("<html><head><title>Test Results</title></head><body>"); writer.write("<h1>Test Execution Results</h1>"); for (ISuite suite : suites) { Map<String, ISuiteResult> suiteResults = suite.getResults(); for (ISuiteResult suiteResult : suiteResults.values()) { // 整合所有测试结果 List<ITestResult> allResults = suiteResult.getTestContext().getPassedTests().getAllResults(); allResults.addAll(suiteResult.getTestContext().getFailedTests().getAllResults()); allResults.addAll(suiteResult.getTestContext().getSkippedTests().getAllResults()); for (ITestResult result : allResults) { String methodId = result.getMethod().getQualifiedName(); String status = getResultStatus(result); // 测试方法标题,绑定点击事件 writer.write(String.format("<div class='test-item'>" + "<h3 onclick='toggleLogs(\"%s\")'>%s - %s</h3>", methodId, result.getMethod().getMethodName(), status)); // 隐藏的日志区域 writer.write(String.format("<div id='%s-logs' style='display:none; padding-left:20px; margin-top:8px;'>", methodId)); List<String> logs = logCollector.getMethodLogs().get(methodId); if (logs != null && !logs.isEmpty()) { writer.write("<h4>Reporter Output:</h4><ul>"); for (String log : logs) { writer.write("<li>" + log + "</li>"); } writer.write("</ul>"); } else { writer.write("<p>No reporter logs available.</p>"); } writer.write("</div></div><hr>"); } } } // 添加切换日志显示的JS逻辑 writer.write("<script>"); writer.write("function toggleLogs(methodId) {"); writer.write(" const logDiv = document.getElementById(methodId + '-logs');"); writer.write(" logDiv.style.display = logDiv.style.display === 'none' ? 'block' : 'none';"); writer.write("}"); writer.write("</script>"); writer.write("</body></html>"); } catch (IOException e) { e.printStackTrace(); } } private String getResultStatus(ITestResult result) { return switch (result.getStatus()) { case ITestResult.SUCCESS -> "<span style='color:green;'>PASSED</span>"; case ITestResult.FAILURE -> "<span style='color:red;'>FAILED</span>"; case ITestResult.SKIP -> "<span style='color:orange;'>SKIPPED</span>"; default -> "UNKNOWN"; }; } }步骤3:配置TestNG启用自定义组件
在testng.xml中注册监听器和报告器,确保两者联动共享日志数据:<suite name="TestSuite"> <listeners> <listener class-name="com.yourpackage.TestLogCollector"/> <listener class-name="com.yourpackage.CustomTestReporter"/> </listeners> <test name="TestCase"> <classes> <class name="com.yourpackage.YourTestClass"/> </classes> </test> </suite>若通过代码启动TestNG,需要手动将
TestLogCollector实例传入CustomTestReporter,保证日志数据能正确传递。简化替代方案:修改默认报告模板
若不想完全自定义报告,可以找到TestNG jar包中默认的HTML报告模板(路径为org/testng/reporters/html/templates/testng-report.html),修改模板的HTML结构和JS逻辑,将原Reporter output区域的内容拆分绑定到对应测试方法节点,添加点击展开的交互效果。这种方式需要熟悉默认模板的渲染逻辑,适合快速调整现有报告样式。
内容的提问来源于stack exchange,提问作者Georgi Georgiev
相关产品推荐
相关产品推荐

