1. 概述

本文将探讨如何使用TestNG生成自定义日志和报告。

TestNG自带报告功能,可生成HTML/XML格式的报告。若通过maven-surefire-plugin运行测试,报告将采用插件默认格式。除内置报告外,TestNG还提供了灵活的日志信息和报告生成定制机制。

⚠️ 若需了解TestNG基础,可参考此文

2. 自定义日志

先看默认日志效果,执行mvn test命令后输出如下:

Tests run: 11, Failures: 1, Errors: 0, Skipped: 0, 
  Time elapsed: 1.21 sec <<< FAILURE! 
- in TestSuite
whenCalledFromSuite_thanOK(baeldung.com.RegistrationTest)  
Time elapsed: 0.01 sec  <<< FAILURE!
java.lang.AssertionError: Test Failed due to some reason
    at baeldung.com.RegistrationTest.whenCalledFromSuite_thanOK(
      RegistrationTest.java:15)


Results :

Failed tests:
  RegistrationTest.whenCalledFromSuite_thanOK:15 
    Test Failed due to some reason

Tests run: 11, Failures: 1, Errors: 0, Skipped: 0

[ERROR] There are test failures.

这些默认日志存在明显缺陷:

  • ❌ 缺少测试执行顺序信息
  • ❌ 未记录测试开始/结束时间点
  • ❌ 无法关联自定义数据

若需获取包含自定义数据的详细执行结果,可通过实现TestNG的监听接口解决。核心方案有两种:

  1. 实现org.testng.ITestListener接口用于日志记录
  2. 实现org.testng.IReporter接口用于报告生成

下面实现一个简单的自定义日志监听器:

public class CustomisedListener implements ITestListener {
    
    // ...
    @Override
    public void onFinish(ITestContext testContext) {
        LOGGER.info("PASSED TEST CASES");
        testContext.getPassedTests().getAllResults()
          .forEach(result -> {LOGGER.info(result.getName());});
        
        LOGGER.info("FAILED TEST CASES");
        testContext.getFailedTests().getAllResults()
          .forEach(result -> {LOGGER.info(result.getName());});
        
        LOGGER.info(
          "Test completed on: " + testContext.getEndDate().toString());
    }  
 
    //...
}

关键点说明:

  • ✅ 重写onFinish()方法,在所有测试完成后触发
  • ✅ 同理可重写onTestStart()onTestFailure()等方法(详见文档

在XML配置中启用监听器:

<suite name="My test suite">
    <listeners>
        <listener class-name="com.baeldung.reports.CustomisedListener" />
    </listeners>
    <test name="numbersXML">
        <parameter name="value" value="1" />
        <parameter name="isEven" value="false" />
        <classes>
            <class name="baeldung.com.ParametrizedTests" />
        </classes>
    </test>
</suite>

执行后输出增强版日志:

...
INFO CUSTOM_LOGS - Started testing on: Sat Apr 22 14:39:43 IST 2017
INFO CUSTOM_LOGS - Testing: 
  givenNumberFromDataProvider_ifEvenCheckOK_thenCorrect
INFO CUSTOM_LOGS - Tested: 
  givenNumberFromDataProvider_ifEvenCheckOK_thenCorrect Time taken:6 ms
INFO CUSTOM_LOGS - Testing: 
  givenNumberObjectFromDataProvider_ifEvenCheckOK_thenCorrect
INFO CUSTOM_LOGS - Failed : 
  givenNumberObjectFromDataProvider_ifEvenCheckOK_thenCorrect
INFO CUSTOM_LOGS - PASSED TEST CASES
INFO CUSTOM_LOGS - givenNumberFromDataProvider_ifEvenCheckOK_thenCorrect
INFO CUSTOM_LOGS - FAILED TEST CASES
INFO CUSTOM_LOGS - 
  givenNumberObjectFromDataProvider_ifEvenCheckOK_thenCorrect
INFO CUSTOM_LOGS - Test completed on: Sat Apr 22 14:39:43 IST 2017
...

相比默认日志,自定义版本提供了:

  • ✅ 测试执行时间戳
  • ✅ 各测试用例的详细状态
  • ✅ 失败用例的明确标识

3. 自定义报告

通过maven-surefire-plugin运行测试后,默认在target/surefire-reports目录生成HTML/XML报告:

默认测试报告

若需通过TestNG XML文件执行特定测试套件,需在surefire-plugin配置中指定:

<configuration>
    <suiteXmlFiles>
        <suiteXmlFile>
            src\test\resources\parametrized_testng.xml
        </suiteXmlFile>
    </suiteXmlFiles>
</configuration>

现在实现自定义报告,核心步骤:

  1. 实现org.testng.IReporter接口
  2. 重写generateReport()方法
public void generateReport(
  List<XmlSuite> xmlSuites, 
  List<ISuite> suites, String outputDirectory) {
 
    String reportTemplate = initReportTemplate();

    String body = suites
      .stream()
      .flatMap(suiteToResults())
      .collect(Collectors.joining());

    String report
      = reportTemplate.replaceFirst("</tbody>", String.format("%s</tbody>", body));
    saveReportTemplate(outputDirectory, report);
}

方法参数说明:

  • xmlSuites:XML文件中定义的所有测试套件
  • suites:包含测试执行完整信息的列表对象
  • outputDirectory:报告生成目录路径

关键实现细节:

  1. initReportTemplate():加载HTML模板
  2. suiteToResults():调用resultsToRow()生成报告内容
  3. saveReportTemplate():保存最终报告

核心处理逻辑:

private Function<ISuite, Stream<? extends String>> suiteToResults() {
    return suite -> suite.getResults().entrySet()
      .stream()
      .flatMap(resultsToRows(suite));
}

private Function<Map.Entry<String, ISuiteResult>, 
  Stream<? extends String>> resultsToRows(ISuite suite) {
    return e -> {
        ITestContext testContext = e.getValue().getTestContext();

        Set<ITestResult> failedTests 
          = testContext.getFailedTests().getAllResults();
        Set<ITestResult> passedTests 
          = testContext.getPassedTests().getAllResults();
        Set<ITestResult> skippedTests 
          = testContext.getSkippedTests().getAllResults();

        String suiteName = suite.getName();

        return Stream
          .of(failedTests, passedTests, skippedTests)
          .flatMap(results ->
            generateReportRows(e.getKey(), suiteName, results).stream());
    };
}

在XML配置中启用报告器:

<suite name="suite">
    <listeners>
        <listener class-name="com.baeldung.reports.CustomisedReports" />
    </listeners>
    <test name="test suite">
        <classes>
        <class name="baeldung.com.RegistrationTest" />
            <class name="baeldung.com.SignInTest" />
        </classes>
    </test>
</suite>

生成的自定义报告效果:

自定义报告

对比默认surefire报告,自定义报告优势明显:

  • ✅ 单表聚合所有测试结果
  • ✅ 清晰展示通过/失败/跳过状态
  • ✅ 可读性显著提升

4. 总结

本文核心要点:

  1. TestNG默认日志信息有限,需通过ITestListener实现自定义日志
  2. 默认surefire报告可读性不足,建议通过IReporter接口定制
  3. 自定义方案显著提升测试结果的可追溯性和可读性

💡 深入了解TestNG(如测试用例/套件编写)可参考入门指南

本文所有代码示例可在GitHub仓库中获取。


原始标题:Custom Reporting with TestNG | Baeldung