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
相关产品推荐
相关产品推荐

