You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 01:03:35