如何用Selenium/Jest在同一会话的新Chrome窗口中运行并发测试
解决方案:基于会话复用的Jest并发Selenium测试
针对Jest test.concurrent 不等待beforeAll执行完成的问题,我们可以通过全局预初始化认证会话+会话复用的方式实现并发测试,同时避免重复执行认证与发布操作。
核心思路
- 在所有测试(包括并发测试)启动前,提前完成一次认证和站点发布,保存Selenium会话的核心信息(会话ID、服务器地址)。
- 每个并发测试用例基于已保存的会话信息,创建独立的Selenium Driver实例,共享已认证的会话状态,无需重复认证。
- 全局清理阶段统一关闭初始会话,避免资源泄漏。
步骤1:实现全局初始化脚本(Setup)
创建global-setup.js,在Jest启动所有测试前完成认证和会话信息保存:
const { Builder, Browser } = require('selenium-webdriver'); const fs = require('fs').promises; const path = require('path'); module.exports = async () => { // 初始化Driver并完成认证、发布 const driver = new Builder().forBrowser(Browser.CHROME).build(); try { await authticateAndReleaseSite(driver); // 获取会话关键信息:会话ID、Selenium服务器地址、原始窗口句柄 const session = await driver.getSession(); const executor = driver.getExecutor(); const sessionInfo = { sessionId: session.getId(), browserUrl: executor.getAddress(), originalWindow: await driver.getWindowHandle() }; // 写入临时文件供测试用例读取 await fs.writeFile( path.join(__dirname, '.session-cache.json'), JSON.stringify(sessionInfo) ); // 注意:此处不要调用driver.quit(),需保留会话供后续测试复用 } catch (error) { await driver.quit(); throw error; } };
步骤2:实现全局清理脚本(Teardown)
创建global-teardown.js,在所有测试完成后关闭初始会话:
const { Builder, Browser } = require('selenium-webdriver'); const fs = require('fs').promises; const path = require('path'); module.exports = async () => { try { // 读取会话信息 const sessionInfo = JSON.parse( await fs.readFile(path.join(__dirname, '.session-cache.json'), 'utf8') ); // 连接到初始会话并关闭整个浏览器 const driver = await new Builder() .forBrowser(Browser.CHROME) .usingServer(sessionInfo.browserUrl) .withSession(sessionInfo.sessionId) .build(); await driver.quit(); // 清理临时文件 await fs.unlink(path.join(__dirname, '.session-cache.json')); } catch (error) { // 忽略清理阶段的非致命错误 console.warn('清理会话时出现异常:', error.message); } };
步骤3:配置Jest启用全局脚本
修改jest.config.js,指定全局初始化和清理脚本:
module.exports = { globalSetup: './global-setup.js', globalTeardown: './global-teardown.js', testTimeout: 30000, // 根据测试时长调整超时时间 // 其他Jest配置... };
步骤4:编写并发测试用例
在测试文件中,每个并发测试复用已认证的会话,独立创建Driver实例:
const { Builder, Browser } = require('selenium-webdriver'); const fs = require('fs').promises; const path = require('path'); const Product = require('./Product'); describe('Test-Suite', () => { let sessionInfo; // 在当前测试块启动前读取会话缓存(此时全局Setup已完成) beforeAll(async () => { sessionInfo = JSON.parse( await fs.readFile(path.join(__dirname, '.session-cache.json'), 'utf8') ); }); // 并发执行测试用例 test.concurrent.each(testCases)('$testcase', async (testCase) => { // 基于已认证会话创建独立Driver实例 const driver = await new Builder() .forBrowser(Browser.CHROME) .usingServer(sessionInfo.browserUrl) .withSession(sessionInfo.sessionId) .build(); const product = new Product(driver); let testWindow; try { // 打开独立窗口执行当前测试,避免并发干扰 testWindow = await driver.switchTo().newWindow('window'); // 执行具体测试逻辑 // await product.runTest(testCase); } finally { // 清理当前测试的窗口 if (testWindow) { await driver.switchTo().window(testWindow); await driver.close(); await driver.switchTo().window(sessionInfo.originalWindow); } // 断开Driver连接(不关闭全局会话) await driver.dispose(); } }); });
关键注意事项
- 会话复用兼容性:确保使用的Selenium Driver(如ChromeDriver)支持会话复用,不同浏览器的配置可能略有差异。
- 并发隔离:每个测试用例必须使用独立的窗口执行,避免多个并发测试操作同一个DOM导致的竞争问题。
- 超时配置:并发测试可能需要更长的超时时间,需在Jest配置中调整
testTimeout。 - 资源限制:根据机器性能调整Jest并发测试的数量(通过
--maxWorkers参数),避免浏览器资源耗尽。
内容的提问来源于stack exchange,提问作者GST
相关产品推荐
相关产品推荐

