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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:42:41