返回首页

在 CI 中稳定 Playwright 测试

本文描述了使用 Dependency Projects、web-first asserts 和正确定位器选择来稳定 CI 中 Playwright 测试的方法论。为企业项目提供配置和代码示例。

Playwright:使用 Dependency Projects 消除 CI 中的不稳定测试
Advertisement 728x90

稳定CI中的Playwright测试:依赖项目与网页优先断言

Playwright中的链式依赖可防止在不稳定环境中执行测试,节省CI资源并减少误报。

首先通过API进行认证,并保持存储状态。随后执行健康检查以验证API可用性。只有在这些步骤完成后,才会运行主UI测试。

相比globalSetup的优势:

Google AdInline article slot
  • 完整的测试轨迹,包含网络请求、截图和控制台日志。
  • 快速失败机制:若设置失败,依赖项目立即中止。
  • 隔离性:可使用 npx playwright test --project=auth-setup 单独测试各阶段。

playwright.config.ts 中的配置:

export default defineConfig({
  projects: [
    {
      name: 'auth-setup',
      testMatch: /.*\.auth\.setup\.ts/,
    },
    {
      name: 'healthcheck',
      testMatch: /.*\.health\.setup\.ts/,
      dependencies: ['auth-setup'],
    },
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
      dependencies: ['healthcheck'],
    },
  ],
});

基于API的认证将设置时间大幅缩短:一次POST请求仅需50–100毫秒,而UI登录需2–5秒。

auth-setup测试示例:

Google AdInline article slot
test('setup', async ({ request }) => {
  await request.post('/api/login', { data: { user, pass } });
  await request.storageState({ path: '.auth/user.json' });
});

依赖图谱可消除并行测试运行时的竞争条件。

智慧选择定位器

定位器的选择直接影响测试对UI变更的抗性。按用途区分:用 getByTestId 处理操作,用 getByRolegetByLabel 进行断言。

最佳实践:

Google AdInline article slot
  • 操作(点击、输入): page.getByTestId('add-to-cart-button').click() —— 与文本或样式无关。
  • 文本/状态验证: expect(page.getByRole('heading')).toHaveText('我的账户')
  • 输入框: page.getByLabel('邮箱').fill('[email protected]')
  • 计数器: expect(locator).toHaveText('3')

CSS选择器仅作为备选方案。培训团队正确使用定位器,在大规模测试中回报可达十倍以上。

网页优先断言 vs 不稳定性

await loc.isVisible() 这类快照检查容易受竞争条件影响。采用轮询机制的网页优先断言更具稳定性。

断言对比:

| 检查项 | 快照(无等待) | 网页优先(轮询) |

|-------|----------------|------------------|

| 可见性 | await loc.isVisible() | expect(loc).toBeVisible() |

| 文本 | await loc.textContent() | expect(loc).toHaveText('文本') |

| 数量 | await loc.count() | expect(loc).toHaveCount(3) |

| 属性 | await loc.getAttribute('href') | expect(loc).toHaveAttribute('href', '/path') |

| 复选框 | await loc.isChecked() | expect(loc).toBeChecked() |

| 状态 | await loc.isEnabled() | expect(loc).toBeEnabled() |

轮询每100毫秒重试一次,直到超时,忽略临时的UI闪烁。

水合与 force: true

在SSR/SSG应用中,当服务端渲染的标记存在但JS处理器尚未加载时,会出现水合不匹配问题。

等待水合标记:

  • await page.waitForSelector('.hydrated', { state: 'attached' })
  • await expect(page.locator('#global-loader')).toBeHidden()

force: true 会禁用可操作性检查(可见、稳定、启用、事件接收),使测试变为原始DOM脚本。应谨慎使用,并始终添加注释——适用于文件上传等边缘场景。

核心要点

  • 依赖项目 构建依赖图谱,防止测试在环境未就绪时运行。
  • 网页优先断言 结合轮询机制,可消除80%由竞争条件引起的不稳定性。
  • 按角色分离定位器 提升对UI重构的适应能力。
  • API认证 相比UI登录,设置速度提升20–50倍。
  • 轨迹优于截图,为CI中的失败提供完整上下文。

— Editorial Team

Advertisement 728x90

继续阅读