自动化测试构建器
目标:测试只在产品被破坏时失败,并且一眼就能告诉你失败原因。
开始编码前
- 找到现有配置:
playwright.config.*、pytest.ini/pyproject.toml、fixtures、helper、页面对象、测试数据。在已有基础上扩展。不要增加第二个框架。 - 选择源用例。如果没有,先编写用例(步骤和预期结果)作为测试描述,然后再编写代码。
- 决定测试层级。使用能捕获 bug 的最廉价层级:
| 你想要验证的内容 | 层级 |
|---|---|
| 业务规则、校验、权限 | API 测试 |
| 用户能在真实 UI 中完成一个流程 | 每个流程一个 E2E 测试 |
| 一个组件在多种状态下的渲染 | 组件测试(如果项目有) |
| 数据确实发生了变化 | UI 步骤后通过 API 或 DB 检查 |
如果项目尚没有测试框架,Web 和 API 从 Playwright Test 开始,Python 服务从 pytest + requests 开始。保持第一个提交小巧:配置、一个 fixture、一个能通过的测试、一个 CI 作业。
定位器
按以下顺序优先选择:
getByRole(role, { name }),这也是一种可访问性检查。getByLabel、getByPlaceholder、getByText用于静态文本。getByTestId当标记不语义化时使用。请要求开发人员添加data-testid,而不是使用 CSS 路径。
永远不要按索引使用 XPath、自动生成的类名或 nth(),除非在注释中说明原因。
等待
- 不要使用
waitForTimeout或sleep。等待状态:await expect(locator).toBeVisible()、page.waitForResponse(url),或轮询 API(expect.poll)。 - Playwright 中的断言会自动重试。使用
expect(locator)而不是读取文本再比较。 - 如果某操作因为设计原因很慢(如邮件、队列),使用带超时和清晰提示信息的轮询。
测试数据和状态
- 在 fixture 中通过 API 创建测试所需的数据,并在测试后清理。
- 每次运行使用唯一数据:用时间戳或
test.info().workerIndex作为后缀。 - 使用
storageState为每个角色登录一次,而不是在每个测试中通过 UI 登录。 - 永远不要依赖测试之间的顺序。每个测试都必须能通过
--grep单独运行。
结构
tests/
api/ 快速检查规则和权限
e2e/ 用户流程,数量少且重要
fixtures/ 认证、数据构建器、API 客户端
pages/ 页面对象,仅当页面被 3 个以上测试使用时才创建
按行为命名测试:viewer cannot delete a project,而不是 test_delete_3。
每个测试只测一种行为。多个 expect 行是允许的,只要它们描述的是同一种行为。
示例在 examples/ 目录中:一个带 API 设置的 UI 流程、一个 API 权限测试、一个 pytest 版本。
第三方和副作用
使用 page.route 或测试替身来桩化支付提供商、邮件、分析和地图,除非测试本身就是针对那个集成。在真实集成测试上添加标签(@integration),以便 CI 可以单独运行它们。
修复不稳定测试
- 复现:
npx playwright test <file> --repeat-each=20 --workers=4。 - 打开失败运行的 trace(
--trace on),找到与通过运行不同的第一步。 - 常见原因:固定等待、定位器匹配了多个元素、worker 之间共享数据、动画、断言执行后才完成的请求。
- 修复原因。CI 中的重试是安全网,不是修复。永远不要将测试标记为跳过,除非在跳过原因中附上问题单链接。
完成定义
- 测试能单独通过且在全套件中通过,连续三次运行。
- 你见过它失败:破坏断言或应用状态一次,并检查失败消息是否合理。
- 代码中没有密钥。凭据来自环境变量。
- 在报告中,测试名称、用例 ID(通过注释或标签)和预期结果是可读的。
报告内容包括:修改的文件、如何运行它们、测试覆盖了什么、以及任何你未能自动化及原因。