定位断言
Day 08 - Assertions
今日目标
- 掌握常用 web-first assertions。
- 知道什么时候断言页面、元素、URL、文本、数量。
学习时间安排(60–120 分钟)
| 时间 | 模块 | 做什么 |
|---|---|---|
| 0-20 分钟 | 核心概念与词汇 | 读概念表,区分页面断言、元素断言、数量断言。 |
| 20-45 分钟 | 官方文档阅读 | 读 Assertions 文档的通用断言和 locator 断言部分。 |
| 45-75 分钟 | 实操练习 | 给 Day 5 的 smoke tests 增加 8 种断言。 |
| 75-105 分钟 | 示例代码改写 | 把普通断言替换成 web-first assertion 并说明差异。 |
| 105-120 分钟 | 复盘与作业 | 记录最常用的 3 个断言,完成今日问题。 |
核心概念与词汇
| English | 中文 | 场景用法 |
|---|---|---|
| web-first assertion | 网页优先断言 | Use it when you explain why expect retries until the condition is met or times out. |
toBeVisible() | 可见断言 | Use it when you verify an element is rendered and visible to the user. |
toBeEnabled() | 可用断言 | Use it when you verify a control is interactive, not disabled. |
toHaveText() | 文本全等断言 | Use it when the element text must equal the expected string exactly. |
toContainText() | 包含文本断言 | Use it when the element text only needs to contain a substring. |
toHaveCount() | 数量断言 | Use it when you verify how many matching elements exist. |
toHaveAttribute() | 属性断言 | Use it when you verify href, value, class, or data attributes. |
toHaveURL() | URL 断言 | Use it when you verify navigation, redirects, or query parameters. |
toHaveTitle() | 标题断言 | Use it when you verify the tab title. |
| auto-retry | 自动重试 | Use it when you explain why assertions survive slow UI updates. |
| positive assertion | 正向断言 | Use it when you assert the expected state appears. |
| negative assertion | 反向断言 | Use it when you assert an element is hidden or absent. |
学习材料
- 必读:[Assertions](https://playwright.dev/docs/test-assertions)
- 必读:[Auto-waiting](https://playwright.dev/docs/actionability) 中关于断言重试的说明
- 选读:[Expect API](https://playwright.dev/docs/api/class-playwrightassertions)(浏览一遍可用断言清单)
重点理解
常用断言:
await expect(page).toHaveTitle(/Playwright/);
await expect(page).toHaveURL(/.*intro/);
await expect(locator).toBeVisible();
await expect(locator).toBeEnabled();
await expect(locator).toHaveText('Success');
await expect(locator).toContainText('Success');
await expect(locator).toHaveCount(3);
await expect(locator).toHaveAttribute('href', '/docs/intro');
注意:
- Playwright 的 web-first assertions 会自动重试。
- 不要用
expect(await locator.textContent()).toBe(...)替代 web-first assertion,除非有明确原因。 - 断言应该表达业务期望,而不是实现细节。
实操步骤
为 Day 5 的 smoke tests 增加断言:
- 标题断言。
- URL 断言。
- 按钮可见断言。
- 文本断言。
- 列表数量断言。
示例代码
import { test, expect } from '@playwright/test';
test('order page shows correct summary', async ({ page }) => {
await page.goto('/orders/42');
await expect(page).toHaveTitle(/Order #42/);
await expect(page).toHaveURL(/\/orders\/42/);
const status = page.getByRole('status');
await expect(status).toBeVisible();
await expect(status).toContainText('Paid');
await expect(page.getByRole('listitem')).toHaveCount(3);
await expect(page.getByRole('button', { name: 'Ship' })).toBeEnabled();
});
常见坑
- 用
expect(await locator.textContent()).toBe('Success')手动取值比较,丢掉自动重试,遇到慢加载就 flaky。 - 用
toBeTruthy()类断言包着isVisible(),报错信息看不出页面状态。 toHaveText和toContainText混用:完整校验用前者,部分匹配用后者。- 断言写实现细节(如 class 名、DOM 结构),UI 重构后全红。
- 只写操作不写断言,测试“跑通”却什么都没验证。
今日产出
- 至少 8 个不同断言示例。
- 记录哪个断言最常用。
今日问题
- Web-first assertion 和普通 assertion 有什么区别?
- 为什么
toBeVisible()比检查 CSS class 更好? toHaveText和toContainText有什么区别?toHaveCount适合哪些场景?- 断言过多和过少分别有什么问题?
复盘要点
- 断言是测试的“价值部分”:没有断言的测试只是操作脚本。
- 好的断言描述用户看到的结果(可见、可点、文案正确、数量正确),而不是 DOM 长相。
- 记住自动重试机制:断言失败前会持续重试到超时,这是 Playwright 稳定性的根基之一。
AI 时代扩展:AI 辅助 Playwright 测试
新增概念
| English | 中文 |
|---|---|
| assertion coverage | 断言覆盖 |
| expected result | 预期结果 |
| over-assertion | 过度断言 |
| assertion strategy | 断言策略 |
适用场景
让 AI 检查你的测试“操作了很多但验证了什么”,或者根据需求描述生成候选断言。注意:哪些业务结果是关键验证点,必须由 QA 自己判断。
可复用表达 / 提示词
我的测试只做了点击和填写,请根据「提交后应显示成功提示并跳转到列表页」这个预期,补充最少且必要的断言。
下面这条断言验证的是实现细节还是用户可见结果?请给出理由和替代方案。
追问加练
- AI 建议断言某个 class 名,你会接受吗?为什么?
- 一条测试里 10 个断言和 2 个断言,哪个更难维护?
- AI 无法替你判断的断言是什么类型的?
今日作业
- 给 Day 5 的 smoke.spec.ts 每条测试补充至少 2 个业务断言。
- 整理“断言速查表”:8 个常用断言 + 场景 + 一个反面例子。
- 让 AI 审查你的断言,找出验证实现细节的地方并修改。