Cucumber7+JUnit5并行测试场景失败后报告异常求助
问题
基于Cucumber7+JUnit5实现测试场景并行执行,共34个场景,JUnit配置中设置cucumber.execution.parallel.config.fixed.parallelism=2。当前遇到以下问题:
- 若某场景因脚本未处理或会话挂起失败,对应的浏览器实例会变为孤儿进程;
- 后续第二个浏览器执行完成后会重新打开两个浏览器(此部分正常);
- 最终Cucumber报告会将该失败场景之后的所有场景标记为失败;
- 所有场景通过时报告显示正常。
使用Cucumber HTML报告+JSON报告(JSON用于Azure),相关代码如下:
DriverFactory 核心代码
public static ThreadLocal<WebDriver> tldriver = new ThreadLocal<>(); public static WebDriver getdriver() { System.out.println("property file read"); String browserName = prop.getProperty("Browser"); logger.info("Browser read is: " + browserName); if (tldriver.get() == null) { tldriver.set(createDriver(browserName)); logger.info("New WebDriver instance created for thread {}", Thread.currentThread().getId()); } return tldriver.get(); }
public static void quitdriver() { WebDriver driverInstance = tldriver.get(); if (driverInstance != null) { try { driverInstance.quit(); logger.info("WebDriver instance successfully closed for thread {}", Thread.currentThread().getId()); } catch (Exception e) { logger.error("Exception occurred while quitting the driver: " + e.getMessage()); } finally { tldriver.remove(); logger.info("WebDriver instance removed from ThreadLocal variable for thread {}", Thread.currentThread().getId()); } } else { logger.warn("WebDriver instance is null for thread {}", Thread.currentThread().getId()); } }
private static WebDriver createDriver(String browser) { logger.info("Browser value is: {}", browser); WebDriver driver = null; if (browser.equals("chrome")) { WebDriverManager.chromedriver().setup(); driver = new ChromeDriver(options); } else if (browser.equals("firefox")) { WebDriverManager.firefoxdriver().setup(); //driver = new FirefoxDriver(); } else { logger.error("Please pass the correct browser value: {}", browser); } return driver; }
配置与上下文代码
# Junit properties file cucumber.execution.parallel.config.fixed.parallelism=2
// TestContext 依赖注入 public TestContext() throws InterruptedException { pageObjectManager = new PageObjectManager(DriverFactoryMy.tldriver); scenarioContext = new ScenarioContext(); }
// PageObject Manager public PageObjectManager(ThreadLocal<WebDriver> driver) { System.out.println("Thread ID in PageObjectManager is == " + Thread.currentThread().getId()); this.driver = driver; } public LoginPage getLoginPage() { return (loginPage == null) ? loginPage = new LoginPage(driver) : loginPage; }
既然每个Driver实例都是独立的,请问如何避免报告受此问题影响?
解决方案
1. 强制场景级别的Driver生命周期隔离
用Cucumber的@Before和@After钩子严格绑定Driver的创建与销毁,确保无论场景成功或失败,都彻底清理Driver实例:
public class Hooks { private final TestContext testContext; // 通过Cucumber DI注入TestContext public Hooks(TestContext testContext) { this.testContext = testContext; } @Before public void beforeScenario() { // 场景开始前主动创建Driver,避免懒加载的状态残留 DriverFactoryMy.getdriver(); } @After(order = 1) public void afterScenario(Scenario scenario) { // 无论场景结果如何,强制销毁Driver DriverFactoryMy.quitdriver(); // 兜底:如果Driver quit失败,直接杀死浏览器进程(以Chrome为例) try { Runtime.getRuntime().exec("taskkill /F /IM chrome.exe /T"); } catch (IOException e) { logger.error("Failed to kill Chrome process: " + e.getMessage()); } } }
2. 修复PageObject的单例污染问题
当前PageObjectManager中LoginPage是单例模式,会导致跨场景复用PageObject实例,造成状态污染。改为每个场景创建独立的PageObject实例:
public class PageObjectManager { private final ThreadLocal<WebDriver> driver; private LoginPage loginPage; public PageObjectManager(ThreadLocal<WebDriver> driver) { this.driver = driver; } public LoginPage getLoginPage() { // 每次获取都新建实例,彻底避免跨场景污染 return new LoginPage(driver); } // 新增清空方法,在@After钩子中调用(如果保留懒加载模式) public void clearPageObjects() { loginPage = null; } }
同时在@After钩子中调用pageObjectManager.clearPageObjects(),确保场景结束后清空PageObject实例。
3. 增强Driver有效性检查
在getdriver()中增加Driver会话有效性校验,避免复用已失效的Driver实例:
public static WebDriver getdriver() { String browserName = prop.getProperty("Browser"); logger.info("Browser read is: " + browserName); WebDriver currentDriver = tldriver.get(); // 检查Driver是否存在且会话有效 if (currentDriver == null || isDriverSessionInvalid(currentDriver)) { // 销毁旧实例(如果存在) if (currentDriver != null) { try { currentDriver.quit(); } catch (Exception e) { logger.error("Failed to quit invalid driver: " + e.getMessage()); } } // 创建新实例 tldriver.set(createDriver(browserName)); logger.info("New WebDriver instance created for thread {}", Thread.currentThread().getId()); } return tldriver.get(); } // 校验Driver会话是否有效 private static boolean isDriverSessionInvalid(WebDriver driver) { try { // 尝试获取当前窗口句柄,若抛异常则会话失效 driver.getWindowHandle(); return false; } catch (WebDriverException e) { logger.warn("Driver session is invalid: " + e.getMessage()); return true; } }
4. 确保TestContext的场景级隔离
Cucumber的PicoContainer DI默认是场景级别的,需确保:
- 不要将
TestContext设为静态类; PageObjectManager和ScenarioContext都是TestContext的非静态成员变量,保证每个场景都会创建新的TestContext实例。
5. 调整并行线程池配置(可选)
如果线程复用导致状态残留,可尝试改用动态线程池配置:
# 根据CPU核心数动态调整并行数,避免固定线程的状态残留 cucumber.execution.parallel.config.dynamic.max-parallelism=2
内容的提问来源于stack exchange,提问作者Vaibhav
相关产品推荐
相关产品推荐

