Playwright测试中setup函数被调用两次的原因及单次调用方案
Playwright + TypeScript 测试中
getInsureApplications()被重复调用的问题 在基于TypeScript的Playwright(@playwright/test)端到端测试项目中,测试设置里的getInsureApplications()函数被调用了两次,但预期仅调用一次。该函数用于生成测试数据,初始化insureApplications数组。
代码片段
// test.spec.ts import { test } from '@playwright/test' export const insureApplications = getInsureApplications() function getInsureApplications(): any[] { console.log('...getInsureApplications() called...') return ['A', 'B'] } for (let i = 0; i < insureApplications.length; i++) { test.only(`Email ${i}`, async () => { // Test logic... }) }
预期与实际行为
- 预期行为:
getInsureApplications()在测试设置阶段仅调用一次,生成测试数据数组。 - 实际行为:运行测试时,控制台多次打印日志,显示该函数被调用两次。
环境信息
- Node版本:v20.11.0
- Playwright版本:1.41.2
问题原因与解决方案
问题根源在于Playwright的运行机制:它会先加载测试文件收集测试用例(此时执行顶层的getInsureApplications()调用),之后在实际运行测试时,会再次加载测试文件,导致顶层代码被执行第二次。
方法1:用test.beforeAll初始化数据
把数据初始化逻辑放到test.beforeAll钩子中,确保只在测试启动前执行一次:
// test.spec.ts import { test } from '@playwright/test' let insureApplications: any[]; test.beforeAll(() => { insureApplications = getInsureApplications(); }); function getInsureApplications(): any[] { console.log('...getInsureApplications() called...') return ['A', 'B'] } // 提前明确数组长度,或结合其他方式动态生成测试用例 for (let i = 0; i < 2; i++) { test.only(`Email ${i}`, async () => { console.log(insureApplications[i]); // Test logic... }) }
方法2:使用参数化测试test.describe.each
如果是要根据数据生成测试用例,推荐用Playwright原生的参数化语法,避免顶层代码重复执行:
// test.spec.ts import { test } from '@playwright/test' function getInsureApplications(): any[] { console.log('...getInsureApplications() called...') return ['A', 'B'] } // 通过describe.each参数化测试,确保数据只生成一次 test.describe.each(getInsureApplications())('Email test', (app) => { test.only(`Email for ${app}`, async () => { // Test logic using 'app'... }) })
方法3:缓存生成的测试数据(适用于数据生成成本高的场景)
把生成的数据写入临时文件,后续直接读取缓存内容,避免重复生成:
// test.spec.ts import { test } from '@playwright/test' import fs from 'fs/promises' import path from 'path' async function getInsureApplications(): Promise<any[]> { const cacheFile = path.join(__dirname, 'test-data.json'); try { const data = await fs.readFile(cacheFile, 'utf-8'); return JSON.parse(data); } catch (e) { console.log('...getInsureApplications() called...') const data = ['A', 'B']; await fs.writeFile(cacheFile, JSON.stringify(data)); return data; } } // 结合beforeAll读取缓存数据 let insureApplications: any[]; test.beforeAll(async () => { insureApplications = await getInsureApplications(); }); for (let i = 0; i < insureApplications.length; i++) { test.only(`Email ${i}`, async () => { // Test logic... }) }
内容的提问来源于stack exchange,提问作者Drashty Dadhaniya
相关产品推荐
相关产品推荐

