项目结构
Day 12 - Hooks and Test Organization
今日目标
- 掌握
beforeEach、afterEach、describe。 - 学会组织测试文件和测试套件。
学习时间安排(60–120 分钟)
| 时间 | 模块 | 做什么 |
|---|---|---|
| 0-20 分钟 | 核心概念与词汇 | 读概念表,区分 beforeEach/beforeAll 的执行时机。 |
| 20-45 分钟 | 官方文档阅读 | 读 Writing tests 的 describe 和 hooks 部分。 |
| 45-75 分钟 | 实操练习 | 把前 10 天的测试按模块重组。 |
| 75-105 分钟 | 示例代码改写 | 抽取重复的前置步骤到 beforeEach。 |
| 105-120 分钟 | 复盘与作业 | 完成今日问题,检查测试是否相互独立。 |
核心概念与词汇
| English | 中文 | 场景用法 |
|---|---|---|
describe | 测试套件分组 | Use it when you group related tests under a readable suite name. |
beforeEach | 每个用例前执行 | Use it when every test needs the same setup, like navigation. |
afterEach | 每个用例后执行 | Use it when you clean up or capture state after each test. |
beforeAll | 套件前执行一次 | Use it with caution when you set up shared state for a suite. |
afterAll | 套件后执行一次 | Use it when you tear down shared resources after a suite. |
| test suite | 测试套件 | Use it when you refer to a group of tests in one describe or file. |
| setup | 前置准备 | Use it when you describe what must be true before a test runs. |
| teardown | 后置清理 | Use it when you describe cleanup after a test. |
| test independence | 测试独立性 | Use it when each test can run alone in any order. |
| shared state | 共享状态 | Use it when state leaks between tests and causes flakiness. |
| serial mode | 串行模式 | Use it when a group of tests must run in order (last resort). |
| nested describe | 嵌套分组 | Use it when you group suites by module and then by feature. |
学习材料
- 必读:[Writing tests - Test hooks](https://playwright.dev/docs/writing-tests#hooks)
- 必读:[Writing tests - Tests in a file](https://playwright.dev/docs/writing-tests#tests-in-a-file)
- 选读:[Serial mode](https://playwright.dev/docs/test-parallel#serial-mode)(了解但不优先使用)
重点理解
示例:
import { test, expect } from '@playwright/test';
test.describe('Docs navigation', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('open intro page', async ({ page }) => {
await page.getByRole('link', { name: 'Get started' }).click();
await expect(page).toHaveURL(/intro/);
});
test('open docs page', async ({ page }) => {
await page.getByRole('link', { name: 'Docs' }).click();
await expect(page).toHaveURL(/docs/);
});
});
注意:
beforeEach适合每个测试都需要的前置动作。- 不要让测试依赖上一个测试执行结果。
beforeAll要谨慎,因为共享状态可能污染测试。
实操步骤
把前 10 天的测试按模块整理:
tests/
smoke.spec.ts
navigation.spec.ts
form.spec.ts
每个文件使用 describe 分组。
示例代码
// navigation.spec.ts
test.describe('Navigation', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test.describe('from homepage', () => {
test('go to docs', async ({ page }) => {
await page.getByRole('link', { name: 'Docs' }).click();
await expect(page).toHaveURL(/docs/);
});
test('go to api reference', async ({ page }) => {
await page.getByRole('link', { name: 'API' }).click();
await expect(page).toHaveURL(/api/);
});
});
});
常见坑
beforeAll里创建数据,被并行测试抢用或互相删除。- 测试 A 创建的数据给测试 B 用,单独跑 B 时必挂。
- hooks 里塞进大量逻辑,失败时不知道是 setup 挂了还是测试挂了。
- 所有测试共用一个 describe,报告里看不出模块结构。
- 用
--grep时忘记 describe 前缀,匹配不到想要的用例。
今日产出
- 重构后的测试目录。
- 至少一个
beforeEach示例。
今日问题
beforeEach和beforeAll有什么区别?- 为什么测试之间不能互相依赖?
describe的主要作用是什么?- 测试文件按页面分好,还是按业务流程分好?
- Hooks 里放太多逻辑有什么风险?
复盘要点
- hooks 的边界:重复的前置动作进 beforeEach,共享的昂贵资源才考虑 beforeAll。
- 组织方式的判断标准:一个新同学看文件名和 describe 名,能不能猜到测试测什么。
- 每加一个 hook 都问自己:这会让测试更难独立运行吗?
AI 时代扩展:AI 辅助 Playwright 测试
新增概念
| English | 中文 |
|---|---|
| test refactoring | 测试重构 |
| suite structure | 套件结构 |
| setup minimization | 前置最小化 |
适用场景
让 AI 帮你把平铺的测试重组成 describe 结构,或者审查 hooks 里是否有重复逻辑和隐藏依赖。
可复用表达 / 提示词
我的 3 个文件共 15 条测试,请按模块提出 describe 分组方案,并指出哪些前置步骤应放入 beforeEach。
请审查我的 hooks,指出哪些逻辑放入 beforeEach 有风险,以及测试间是否有隐藏依赖。
追问加练
- AI 建议把登录放进 beforeEach,什么场景下这是错的?
- 按页面分文件和按流程分文件,AI 通常怎么建议?你的项目适合哪种?
- 重构后的测试还能单独运行吗?你如何验证?
今日作业
- 完成测试目录重组,每个文件有 describe 分组。
- 找出并删除测试间的数据依赖,保证任意单条可运行。
- 让 AI 审查你的目录结构和 hooks,采纳合理建议并记录理由。