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

Selenium+Spring Boot多测试用例@Autowired WebDriver Bean问题求助

问题描述

我正在搭建Selenium+Spring Boot自动化测试项目,已编写测试用例Test#1和Test#2。单独运行Test#1时一切正常,但运行完Test#1后再运行Test#2,WebDriver出现异常。推测问题出在@Bean注解配置的WebDriver上,当前@Autowired注入的WebDriver Bean无法支持多测试用例场景,求问如何配置可支持多测试用例的@Autowired WebDriver Bean。

附原项目代码:

1) WebDriver配置类(含Bean)

@Configuration
public class WebDriverConfig {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "browser", havingValue = "chrome")
@Primary
public WebDriver chromeDriver() {
    WebDriverManager.chromedriver().setup();
    return new ChromeDriver();
}

2) BasePage类

@Component
public abstract class BasePage {

@Autowired
protected WebDriver driver;
protected final static long WAIT_TIME = 20;

@PostConstruct
private void init() {
    PageFactory.initElements(this.driver, this);
}

3) 包含测试方法的HomePage类

@Component
public class HomePage extends BasePage {
@FindBy(xpath = "//a[@class='login_btn circle']")
private WebElement singIn;

@FindBy(xpath = "//*[@id='login_input1']")
private WebElement inputLogin;

@FindBy(xpath = "//*[@id='login_input2']")
private WebElement inputPassword;

@FindBy(css = "#login_submit")
private WebElement loginSubmit;

@FindBy(xpath = "//a[.//*[@id='anime_id_17']]")
private WebElement tg;

@FindBy(xpath = "//a[.//*[@id='anime_id_12']]")
private WebElement codeGias;

public HomePage goToHomePage(String url) {
    driver.get(url);
    waitForPageLoadComplete(WAIT_TIME);
    return this;
}

public HomePage goToLogin(String name, String password) {
    singIn.click();
    waitForPageLoadComplete(WAIT_TIME);
    inputLogin.clear();
    inputLogin.sendKeys(name);
    inputPassword.clear();
    inputPassword.sendKeys(password);
    loginSubmit.click();
    waitForPageLoadComplete(WAIT_TIME);
    tg.click();
    waitForPageLoadComplete(WAIT_TIME);
    return this;
}
public HomePage test(){
    waitForPageLoadComplete(WAIT_TIME);
    codeGias.click();
    waitForPageLoadComplete(WAIT_TIME);
    return this;
}

4) BaseTest类

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SpringSelApplication.class)
public class BaseTest {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    public WebDriver driver;

    @Autowired
    public ApplicationContext applicationContext;

    @BeforeEach
    public void setup() {
        driver.manage().window().maximize();
    }

    @AfterEach
    public void teardown() {
        driver.quit();
   }
}

问题原因

Spring Bean默认是**单例(singleton)**作用域,你的WebDriver Bean也不例外。Test#1执行完后,@AfterEach里调用driver.quit()关闭了浏览器进程,此时这个单例WebDriver实例已经失效。当Test#2启动时,Spring仍然注入同一个已失效的WebDriver实例,自然会抛出异常。


解决方案

核心思路是把WebDriver Bean的作用域改为原型(prototype),让Spring每次注入时都创建新的WebDriver实例,同时调整页面类的初始化逻辑,确保每个测试用例都绑定独立的浏览器实例。

1. 修改WebDriver配置类,设置原型作用域

给WebDriver的Bean添加@Scope("prototype")注解,这样每次请求Bean时都会生成新实例:

@Configuration
public class WebDriverConfig {
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "browser", havingValue = "chrome")
    @Primary
    @Scope("prototype") // 关键:改为原型作用域
    public WebDriver chromeDriver() {
        WebDriverManager.chromedriver().setup();
        return new ChromeDriver();
    }
}

2. 调整BasePage及子类的作用域与初始化逻辑

因为BasePage是@Component(默认单例),如果直接注入原型的WebDriver,单例页面类只会在初始化时拿到一次WebDriver实例,后续测试用例无法更新。所以需要把页面类也设为原型作用域,改用构造注入初始化页面元素:

@Component
@Scope("prototype") // 页面类设为原型,每次获取新实例
public abstract class BasePage {

    protected WebDriver driver;
    protected final static long WAIT_TIME = 20;

    // 构造注入WebDriver,替代Autowired字段注入
    @Autowired
    public BasePage(WebDriver driver) {
        this.driver = driver;
        PageFactory.initElements(driver, this); // 在构造方法中初始化页面元素
    }

    // 补充waitForPageLoadComplete方法(原代码中缺失,这里补全示例)
    protected void waitForPageLoadComplete(long timeout) {
        new WebDriverWait(driver, timeout).until(
            webDriver -> ((JavascriptExecutor) webDriver).executeScript("return document.readyState").equals("complete")
        );
    }
}

HomePage类无需额外修改@Scope,会继承BasePage的配置:

@Component
public class HomePage extends BasePage {
    // 保持原有元素和方法不变
    @FindBy(xpath = "//a[@class='login_btn circle']")
    private WebElement singIn;

    // ... 其余元素和方法省略
}

3. 修改BaseTest类,确保每次测试获取新实例

通过ApplicationContext手动获取原型Bean,确保每个测试用例都拿到独立的WebDriver和页面实例:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SpringSelApplication.class)
public class BaseTest {

    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ApplicationContext applicationContext;

    protected WebDriver driver;
    protected HomePage homePage;

    @BeforeEach
    public void setup() {
        // 每次测试都获取新的WebDriver实例
        driver = applicationContext.getBean(WebDriver.class);
        // 获取新的页面实例,绑定当前测试的WebDriver
        homePage = applicationContext.getBean(HomePage.class);
        driver.manage().window().maximize();
    }

    @AfterEach
    public void teardown() {
        if (driver != null) {
            driver.quit(); // 确保关闭浏览器,释放资源
        }
    }
}

注意事项
  • 原型Bean不会被Spring自动销毁,必须在@AfterEach中手动调用driver.quit(),避免浏览器进程泄漏。
  • 如果有其他浏览器的Bean配置(如FirefoxDriver),同样需要添加@Scope("prototype")。
  • 页面类设为原型后,每次从Spring上下文获取都会创建新实例,确保页面元素绑定的是当前测试用例的WebDriver。

内容的提问来源于stack exchange,提问作者Алексей

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 12:30:42