为何Extent Report仅显示最后一个测试套件结果?多套件报告问题
解决Extent Report仅显示最后一个TestNG测试套件结果的问题
这个问题的核心原因很明确:你每个测试套件运行时都会重新初始化ExtentReports和ExtentHtmlReporter实例,并且每个套件结束都执行flush(),导致前一个套件的报告被覆盖,后续套件也无法写入已经关闭的报告流。
下面是具体的修复方案,分步骤来:
1. 用单例模式管理ExtentReports实例
我们需要让所有测试套件共享同一个ExtentReports和ExtentHtmlReporter实例,避免重复初始化覆盖报告。创建一个工具类来管理单例:
public class ExtentManager { private static ExtentReports extent; private static ExtentHtmlReporter htmlReporter; // 线程安全的单例获取方法 public static synchronized ExtentReports getExtent() { return extent; } // 初始化报告,只执行一次 public static void initExtent() { if (extent == null) { htmlReporter = new ExtentHtmlReporter(Utils.getReportDir() + "/report.html"); htmlReporter.loadXMLConfig(String.valueOf(new File("src/test/java/extentreports/extent-config.xml"))); extent = new ExtentReports(); extent.attachReporter(htmlReporter); // 设置系统信息 extent.setSystemInfo("OS Name", System.getProperty("os.name")); extent.setSystemInfo("OS Version", System.getProperty("os.version")); extent.setSystemInfo("Java Version", System.getProperty("java.version")); extent.setSystemInfo("User Name", System.getProperty("user.name")); // 配置报告样式 htmlReporter.config().setChartVisibilityOnOpen(true); htmlReporter.config().setDocumentTitle("Automation"); htmlReporter.config().setReportName("Report"); htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP); htmlReporter.config().setTheme(Theme.DARK); } } // 创建测试用例节点 public static ExtentTest createTest(String testName) { return extent.createTest(testName); } }
2. 用TestNG套件监听器控制报告生命周期
TestNG的@BeforeSuite和@AfterSuite会在每个套件执行时触发,所以我们需要用ISuiteListener来监听所有套件的开始和结束,确保只在第一个套件启动时初始化报告,最后一个套件结束时才flush:
import org.testng.ISuite; import org.testng.ISuiteListener; import java.io.File; import java.util.List; import java.util.stream.Collectors; public class ExtentSuiteListener implements ISuiteListener { @Override public void onStart(ISuite suite) { // 仅在首次启动时初始化报告 ExtentManager.initExtent(); } @Override public void onFinish(ISuite suite) { // 判断当前是否是最后一个执行的套件 List<String> suiteFileNames = suite.getXmlSuite().getSuiteFiles().stream() .map(filePath -> new File(filePath).getName()) .collect(Collectors.toList()); String currentSuiteName = suite.getName(); if (suiteFileNames.indexOf(currentSuiteName) == suiteFileNames.size() - 1) { // 最后一个套件执行完毕,flush报告 ExtentManager.getExtent().flush(); loginAndroid.quitTest(); // 全局驱动在这里关闭 } } }
3. 修改测试类代码
去掉原来的@BeforeSuite和@AfterSuite方法,修改测试用例中创建test的方式,同时调整@AfterMethod使用静态的test变量:
import com.aventstack.extentreports.Status; import com.aventstack.extentreports.markuputils.ExtentColor; import com.aventstack.extentreports.markuputils.MarkupHelper; import org.testng.ITestResult; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.io.IOException; public class YourTestClass { // 测试类中定义静态的ExtentTest变量 public static ExtentTest test; @AfterMethod public void getResult(ITestResult result) { if (result.getStatus() == ITestResult.FAILURE) { test.log(Status.FAIL, MarkupHelper.createLabel(result.getName() + " Test case FAILED due to below issues:", ExtentColor.RED)); test.fail(result.getThrowable()); String screenShotPath = MobileActions.Screenshot("Failed"); try { test.addScreenCaptureFromPath(screenShotPath); } catch (IOException e) { e.printStackTrace(); } } else if (result.getStatus() == ITestResult.SUCCESS) { test.log(Status.PASS, MarkupHelper.createLabel(result.getName() + " Test Case PASSED", ExtentColor.GREEN)); } else { test.log(Status.SKIP, MarkupHelper.createLabel(result.getName() + " Test Case SKIPPED", ExtentColor.ORANGE)); test.skip(result.getThrowable()); } } // 测试用例中创建test的方式 @Test public void testAddImageOnCanvas() { test = ExtentManager.createTest("Add Image on Canvas"); // 你的测试逻辑... } }
4. 配置TestNG XML
在主测试套件的XML中添加我们的监听器,确保所有套件都使用这个监听器:
<suite name="All_Test_Suites"> <listeners> <listener class-name="com.your.package.ExtentSuiteListener"/> <!-- 替换成你的监听器类路径 --> </listeners> <suite-files> <suite-file path="Font_Module.xml"></suite-file> <suite-file path="Image_Module.xml"></suite-file> </suite-files> </suite>
为什么原来的代码不生效?
- 重复初始化覆盖报告:每个套件运行时,
@BeforeSuite都会重新创建ExtentHtmlReporter,这会清空之前的report.html文件,只保留当前套件的结果。 - 提前关闭报告流:每个套件结束时的
@AfterSuite执行extent.flush(),会关闭报告的写入流,后续套件无法再向报告中添加内容。
通过上面的改造,所有测试套件都会共享同一个报告实例,只有在最后一个套件执行完成后才会flush报告,这样所有套件的结果都会被保留下来。
内容的提问来源于stack exchange,提问作者T.Che
相关产品推荐
相关产品推荐

