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,提问作者Алексей

