Playwright中Session Storage配置异常,Cucumber并行测试重复登录求助
解决Cucumber并行测试中重复登录的问题
问题根源
你遇到的核心问题是并行进程之间的文件系统竞争:当多个Cucumber并行进程同时启动时,它们会几乎同时检查sessionStatePath是否存在。此时如果文件还未被第一个进程写入完成,所有进程都会判断文件不存在,进而各自执行登录流程。并行数为4时,这种竞争导致所有进程都触发登录;并行数为2时,前两个进程中有一个先完成文件写入,后续启动的进程就能读取到已存在的Session文件,从而复用登录状态。
解决方案:预先生成Session Storage
最可靠的方式是在所有并行测试执行前,单独生成一次Session Storage文件,确保所有并行进程启动时都能直接复用已有的登录状态,彻底避免重复登录。
步骤1:编写独立的Session生成脚本
创建一个单独的Node.js脚本(比如generate-session.js),专门负责登录并保存Session状态:
const playwright = require('playwright'); const fs = require('fs'); const PageObjectManager = require('./path-to-your-po-manager'); // 替换为你的PO路径 const sessionStatePath = './session-state.json'; // 和hooks中一致的路径 async function generateSession() { const browser = await playwright.chromium.launch({ channel: 'chrome', headless: false }); const context = await browser.newContext({ permissions: ['clipboard-read', 'clipboard-write'] }); const page = await context.newPage(); const poManager = new PageObjectManager(page); const loginPage = await poManager.fetchLoginPage(); const landingPage = await poManager.fetchLandingPage(); // 执行登录流程 await loginPage.gotoLoginPage(); await loginPage.loginToTheApplication(); await landingPage.closeModalDialog(); // 保存Session状态 await context.storageState({ path: sessionStatePath }); console.log('Session state generated successfully.'); await browser.close(); } generateSession().catch(err => { console.error('Failed to generate session:', err); process.exit(1); });
步骤2:修改hooks.js,移除登录逻辑
修改你的Before钩子,直接加载已生成的Session文件,不再判断是否需要登录:
Before(async function (scenario) { log(`Starting Scenario: ${scenario.pickle.name}`) this.browser = await playwright.chromium.launch({ channel: 'chrome', headless: false, isEnabled: () => true, log: logWrapper }) // 直接加载预先生成的Session文件 const context = await this.browser.newContext({ storageState: sessionStatePath, permissions: ['clipboard-read', 'clipboard-write'], recordVideo: { dir: 'videos/' } }) await context.setDefaultTimeout(60000) this.page = await context.newPage() this.poManager = new PageObjectManager(this.page) this.loginPage = await this.poManager.fetchLoginPage() this.landingPage = await this.poManager.fetchLandingPage() this.apiContext = await playwright.request.newContext() this.apiUtil = new APIUtil(this.apiContext) this.dataGenerator = new DataGenerator(this.apiContext) this.backEndHelper = new BackEndHelper(this.apiUtil) // 如果需要JWT Token,可以从Session文件中读取,或者在生成Session时单独保存 // 示例:从Session文件中提取Token const sessionData = JSON.parse(fs.readFileSync(sessionStatePath, 'utf8')); this.jwtToken = sessionData.cookies.find(c => c.name === 'your-jwt-cookie-name')?.value; })
步骤3:调整测试执行命令
先运行Session生成脚本,再启动Cucumber并行测试:
node generate-session.js && yarn cucumber-js path-to-feature.feature --parallel 4
你也可以把这个命令加到package.json的scripts中,方便执行:
{ "scripts": { "test:parallel": "node generate-session.js && yarn cucumber-js path-to-feature.feature --parallel 4" } }
额外优化建议
- Session过期处理:如果登录Session有有效期,可以在生成脚本中添加过期检查,避免使用失效的Session。
- 隔离测试数据:如果测试用例需要操作独立的数据,建议通过API在每个测试前初始化数据,而不是重新登录,确保复用Session的同时测试隔离性。
- 避免重复启动浏览器:可以考虑在多个场景间复用浏览器上下文(但Cucumber并行模式下每个场景是独立进程,所以这个优化可能不适用,预先生成Session是更适合的方案)。
内容的提问来源于stack exchange,提问作者Chetan Naidu
相关产品推荐
相关产品推荐

