Playwright测试块失败时重复执行beforeAll及重新初始化数据的问题解决方案咨询
Playwright测试块失败时重复执行beforeAll及重新初始化数据的问题解决方案咨询
各位Playwright大佬好,我最近在做端到端测试时碰到了一个棘手的问题,想请教下大家怎么解决。
我现在的测试架构是按功能划分测试文件,每个文件覆盖一个完整的功能流程:在模块级别生成随机测试数据(比如学校名称、负责人邮箱),然后通过test.beforeAll钩子一次性初始化数据库,后续所有测试块都依赖这套数据,而且必须按顺序执行(已经在配置里设了workers: 1)。
但只要有任何一个测试块失败——不管是用CLI重试还是在UI模式下恢复执行——Playwright就会重新加载整个测试文件。这直接导致beforeAll再次运行,模块级变量被重新初始化,数据库也被填充了全新的数据。后面的测试块用新数据跑,自然就和之前的上下文不匹配,引发连锁失败。
一开始我以为是Faker的随机数生成有问题,特意换成了自定义的generateId函数验证,结果发现问题根源是Playwright本身的行为:单个测试失败后它会重新加载整个文件,和随机数工具无关。
下面是我的配置和最小复现代码,想请教各位:怎么才能让beforeAll(以及模块级变量)在每个文件的一次运行周期里只执行一次,哪怕有测试块失败也不重复初始化?
我的Playwright配置
export default defineConfig({ testDir: './playwright', workers: 1, reporter: 'html', timeout: 10000, use: { baseURL: 'http://127.0.0.1:3000', screenshot: 'only-on-failure', trace: 'retain-on-failure', testIdAttribute: 'data-test', navigationTimeout: 10000 }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } } ] });
最小复现代码
function generateId(length = 6, separator = '-'): string { const timestamp = +new Date(); const _getRandomInt = (min, max): number => { return Math.floor(Math.random() * (max - min + 1)) + min; }; const ts = timestamp.toString(); const parts = ts.split('').reverse(); let id = ''; for (let i = 0; i < length; ++i) { const index = _getRandomInt(0, parts.length - 1); if (i % 3 === 0 && i && separator) id += separator; id += parts[index]; } return id; } const randomNumber = generateRandomNumber(); faker.seed(randomNumber); console.log('randomNumber', randomNumber); test.describe('Playwright behaviour', () => { const mySchoolName: string = `${generateSchoolName()} School`; const leaderFirstName: string = generateFirstName(); const leaderlastName: string = generateLastName(); const schoolEmail: string = generateUserEmail(leaderFirstName, leaderlastName); const generateIdString: string = generateId(6, '-'); test.beforeAll(async () => { const { accessToken: adminAccessToken } = await getTokenFromLogin(admin, password); await createSchoolAndDummyWondeData(adminAccessToken, { schoolName: mySchoolName, leaderPassword: password, leader: { firstname: leaderFirstName, lastname: leaderlastName, email: schoolEmail, } }); }); test.beforeEach(async ({ page }) => { await page.goto('/'); }); test('Add test data', async ({ page }) => {}); test('block 1', async ({ page }) => { console.log('TEST BLOCK 1'); console.log(`School Name: ${mySchoolName}`); console.log(`Leader Name: ${leaderFirstName} ${leaderlastName}`); console.log(`Leader Email: ${schoolEmail}`); console.log('randomNumber', randomNumber); console.log('generateIdString', generateIdString); await login(page, schoolEmail, password); }); test('block 2', async ({ page }) => { console.log('TEST BLOCK 2'); console.log(`School Name: ${mySchoolName}`); console.log(`Leader Name: ${leaderFirstName} ${leaderlastName}`); console.log(`Leader Email: ${schoolEmail}`); console.log('randomNumber', randomNumber); console.log('generateIdString', generateIdString); await login(page, 'iwillfail@failure.com', password); // 这行必定失败 }); test('block 3', async ({ page }) => { console.log('TEST BLOCK 3'); console.log(`School Name: ${mySchoolName}`); console.log(`Leader Name: ${leaderFirstName} ${leaderlastName}`); console.log(`Leader Email: ${schoolEmail}`); console.log('randomNumber', randomNumber); console.log('generateIdString', generateIdString); await login(page, schoolEmail, password); }); test('block 4', async ({ page }) => { console.log('TEST BLOCK 4'); console.log(`School Name: ${mySchoolName}`); console.log(`Leader Name: ${leaderFirstName} ${leaderlastName}`); console.log(`Leader Email: ${schoolEmail}`); console.log('randomNumber', randomNumber); console.log('generateIdString', generateIdString); await login(page, schoolEmail, password); }); });
我目前想到的几个方向,但还没找到最优解
- 用测试上下文共享状态:把数据存在
test.info()的上下文里,beforeAll先检查上下文是否有数据再决定是否初始化,但不确定Playwright重试时上下文会不会被重置。 - 用文件系统做初始化标记:在
beforeAll里先检查有没有初始化标记文件,有就跳过,没有就执行初始化并创建标记。但要注意清理标记,避免影响下一次测试运行。 - 调整Playwright的重试策略:比如给单个测试禁用重试,但这样就失去了重试的便利性。
有没有大佬有成熟的解决方案,或者更优雅的处理方式?麻烦指点下,谢谢!
内容来源于stack exchange
相关产品推荐
相关产品推荐

