基于extentreports-testng-adapter:Cucumber+TestNG失败用例截图添加至HTML报告
解决方案:Cucumber场景失败时自动附加截图到Extent Report(兼容并行测试)
我明白你的情况:用Cucumber+Selenium+TestNG搭的框架,靠extentreports-testng-adapter自动生成报告,没写额外的Extent代码,还得支持并行测试,现在就差失败场景自动加截图这一步。下面给你一套完美适配的方案:
步骤1:编写自定义Extent监听器
既然你已经在用ExtentITestListenerAdapter,我们直接继承它,重写测试失败时的处理逻辑即可。这个监听器会在每个Cucumber场景(对应TestNG的测试方法)失败时触发,我们在里面嵌入截图逻辑:
import com.aventstack.extentreports.MediaEntityBuilder; import com.aventstack.extentreports.Status; import com.aventstack.extentreports.testng.listener.ExtentITestListenerAdapter; import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.WebDriver; import org.testng.ITestResult; public class CustomExtentListener extends ExtentITestListenerAdapter { @Override public void onTestFailure(ITestResult result) { // 先调用父类的失败处理逻辑,保留原有日志输出 super.onTestFailure(result); // 从测试实例中获取WebDriver——这里假设你的Step类用ThreadLocal存储驱动(并行测试必备) Object testInstance = result.getInstance(); WebDriver driver = ((YourBaseStepClass) testInstance).getDriver(); if (driver != null) { // 生成base64格式的截图(无需存本地,直接嵌入报告) String base64Screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BASE64); // 将截图附加到当前对应的Extent测试条目 getExtentTest().fail("场景失败截图", MediaEntityBuilder.createScreenCaptureFromBase64String(base64Screenshot).build()); } } }
并行测试核心注意事项
必须用ThreadLocal管理WebDriver,避免并行时多个场景争抢同一个驱动实例。比如在你的基础Step类中这么实现:
public class YourBaseStepClass { // 为每个线程独立存储WebDriver实例 private static final ThreadLocal<WebDriver> driverThreadLocal = new ThreadLocal<>(); public WebDriver getDriver() { return driverThreadLocal.get(); } public void setDriver(WebDriver driver) { driverThreadLocal.set(driver); } }
另外,父类提供的getExtentTest()方法会自动返回当前线程对应的ExtentTest实例,完全不用担心并行测试时报告串图的问题。
步骤2:替换测试类中的监听器配置
把原来的ExtentITestListenerAdapter换成我们刚写的自定义监听器即可:
@CucumberOptions( features = { "src/test/resources/common_features/test.feature" }, glue = { "com.step.definitions" }, tags = {}, plugin = {}, monochrome = true, dryRun = false ) @Listeners({CustomExtentListener.class}) // 替换为自定义监听器 public class TestCase extends AbstractTestNGCucumberTests { }
备选方案:结合Cucumber的@After钩子实现
如果你更习惯用Cucumber的钩子体系处理,也可以采用这个方案:
import com.aventstack.extentreports.MediaEntityBuilder; import com.aventstack.extentreports.testng.listener.ExtentITestListenerAdapter; import io.cucumber.java.After; import io.cucumber.java.Scenario; import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.WebDriver; public class Hooks { private final WebDriver driver; private final ExtentITestListenerAdapter extentListener; // 通过Cucumber默认支持的依赖注入(如PicoContainer)获取实例 public Hooks(WebDriver driver, ExtentITestListenerAdapter extentListener) { this.driver = driver; this.extentListener = extentListener; } @After public void afterScenario(Scenario scenario) { if (scenario.isFailed()) { String base64Screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BASE64); // 为当前失败的场景附加截图 extentListener.getExtentTest().fail("场景失败截图", MediaEntityBuilder.createScreenCaptureFromBase64String(base64Screenshot).build()); } } }
这个方案的优势是逻辑和Cucumber的钩子体系结合更紧密,同样要注意用ThreadLocal管理WebDriver以支持并行测试。
内容的提问来源于stack exchange,提问作者Dushyant Patil
相关产品推荐
相关产品推荐

