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

Extent Report 5.0.9生成测试报告时用例被覆盖缺失问题

问题根因

用例被覆盖、报告计数和实际编写数量不一致的核心原因,是你把ExtentTest对象声明成了测试类的全局共享变量:

  • 所有@Test方法执行时都会反复给这个全局test变量重新赋值
  • 哪怕你没有手动开启TestNG并行执行,测试方法执行和@AfterMethod钩子触发的时序也可能出现错位:比如用例A刚完成createTest还没写入执行结果,调度切到用例B执行,test变量就会被覆盖为用例B的实例,后续A的执行日志会错写到B的记录上,A本身因为没有关联任何结果日志,最终生成报告时就会丢失,表现为被其他用例覆盖。你写23个用例丢2个,就是典型的共享变量写入冲突问题。
修复步骤

按以下三点调整代码即可彻底解决该问题:

  • 用ThreadLocal<ExtentTest>替代全局单一的ExtentTest成员变量,保证每个测试方法持有独立的ExtentTest实例,互不干扰
  • 把创建ExtentTest实例的逻辑从@Test方法内部移到@BeforeMethod生命周期方法中,保证每个测试方法执行前就绑定好专属的test实例,避免赋值时序错位
  • 在@AfterMethod中从ThreadLocal取出当前方法对应的test实例写入日志,操作完成后主动移除ThreadLocal中的变量,避免内存泄漏
修复后可直接运行的代码
package Testcases;

import org.testng.annotations.Test;
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.Status;
import com.aventstack.extentreports.reporter.ExtentSparkReporter;
import com.aventstack.extentreports.reporter.configuration.Theme;
import org.testng.Assert;
import org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import java.lang.reflect.Method;

public class sample2 {
    ExtentSparkReporter htmlReporter;
    ExtentReports extent;
    // 用ThreadLocal存储每个测试方法独立的ExtentTest实例,避免共享变量覆盖
    ThreadLocal<ExtentTest> testThreadLocal = new ThreadLocal<>();

    @BeforeTest
    public void startReport() {
        // 修正原代码路径多写一个.的问题,避免报告生成位置异常
        htmlReporter = new ExtentSparkReporter(System.getProperty("user.dir") + "//test-output//Report12.html");
        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);
        htmlReporter.config().setDocumentTitle("Automation Report");
        htmlReporter.config().setReportName("Test report");
        htmlReporter.config().setTimeStampFormat("EEEE, MMMM dd, yyyy, hh:mm a '('zzz')'");
        htmlReporter.config().setTheme(Theme.STANDARD);
    }

    @BeforeMethod
    public void createTestInstance(Method method) {
        // 每个测试方法执行前,提前创建专属的ExtentTest实例,绑定到ThreadLocal
        ExtentTest test = extent.createTest(method.getName(), "Test case for " + method.getName());
        testThreadLocal.set(test);
    }

    @Test
    public void test_1() {
        Assert.assertTrue(true);
    }

    @Test
    public void test_2() {
        Assert.assertTrue(false);
    }

    @Test
    public void test_3() {
        throw new SkipException("The test has been skipped");
    }

    @AfterMethod
    public void getResult(ITestResult result) {
        ExtentTest currentTest = testThreadLocal.get();
        if(result.getStatus() == ITestResult.FAILURE) {
            currentTest.log(Status.FAIL,result.getThrowable());
        }
        else if(result.getStatus() == ITestResult.SUCCESS) {
            currentTest.log(Status.PASS, result.getTestName());
        }
        else {
            currentTest.log(Status.SKIP, result.getTestName());
        }
        // 用完移除,避免内存泄漏
        testThreadLocal.remove();
    }

    @AfterTest
    public void tearDown() {
        extent.flush();
    }
}
补充说明
  • 调整后的写法完全支持TestNG多线程并行执行场景,不会再出现用例覆盖、日志错写、用例丢失的问题
  • 如果需要自定义报告中每个用例的展示名称、描述,可以直接读取@Test注解的对应属性传入createTest方法,不需要在每个测试方法内硬编码。

内容的提问来源于stack exchange,提问作者Nandha Kumar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:42:15