1260 words
6 minutes
每日Skill学习 - E2E Testing Patterns

每日Skill学习 — E2E Testing Patterns#

欸嘿~今天来聊一个超实用的 Skill 吧!就是 E2E Testing Patterns,专门教你怎么用 Playwright 和 Cypress 写靠谱的端到端测试喵~


🎯 这个 Skill 是干啥的?#

简单来说,E2E Testing Patterns 就是一套编写端到端测试的最佳实践集合。它不是教你”怎么写测试”,而是教你”怎么写好测试”——让测试跑得快、不 flaky、能 catch 到真正的问题。

核心目标:

  • 在用户发现 bug 之前就 catch 住它们
  • 测试跑得够快,CI/CD 能愉快地集成
  • 测试稳定,不会时不时抽风 fail
  • 只测关键路径,不过度测试

🔥 亮点功能#

1. 测试金字塔理论#

这个 Skill 首先帮你厘清了一个很重要的问题:什么时候该用 E2E 测试?

/\
/E2E\ ← 少量:只测关键路径
/─────\
/Integr\ ← 适量:组件交互、API 契约
/────────\
/Unit Tests\ ← 大量:快、隔离、覆盖边界情况
/────────────\

E2E 测试适合的场景:

  • ✅ 关键用户旅程(登录 → 控制台 → 操作 → 登出)
  • ✅ 多步骤流程(结算流程、入职引导)
  • ✅ 跨浏览器兼容性
  • ✅ 真实 API 集成
  • ✅ 认证授权流程

E2E 测试不适合的场景:

  • ❌ 单元级别的逻辑(用单元测试)
  • ❌ API 契约测试(用集成测试)
  • ❌ 边界情况(太慢了,用单元测试)
  • ❌ 组件视觉状态(用 Storybook)

💡 经验法则:如果这个功能挂了会让你公司倒闭,那就用 E2E 测;如果只是不方便,用单元/集成测试就行喵~


2. Playwright 核心模式#

Page Object Model#

把页面逻辑封装起来,测试代码读起来像用户故事一样!

pages/LoginPage.ts
export class LoginPage {
readonly emailInput: Locator;
readonly loginButton: Locator;
constructor(page: Page) {
this.emailInput = page.getByLabel("Email");
this.loginButton = page.getByRole("button", { name: "Login" });
}
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.loginButton.click();
}
}
// 测试代码超简洁
test("successful login", async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.login("user@example.com", "password123");
await expect(page).toHaveURL("/dashboard");
});

Fixtures for Test Data#

每个测试创建自己的数据,用完自己清理,测试之间完全隔离!

test("user can update profile", async ({ page, testUser }) => {
// testUser 在测试前自动创建,测试后自动删除
await page.goto("/profile");
await page.getByLabel("Name").fill(testUser.name);
// ...
});

Smart Waiting(重头戏!)#

绝对不要用固定超时,这是 flaky 测试的最大元凶喵!

// ❌ FLAKY: 固定等待,迟早会翻车
await page.waitForTimeout(3000);
// ✅ STABLE: 等待特定条件
await page.waitForLoadState("networkidle");
await page.waitForURL("/dashboard");
// ✅ BEST: 使用自动等待的断言
await expect(page.getByText("Welcome")).toBeVisible();
await expect(page.getByRole("button", { name: "Submit" })).toBeEnabled();

3. 选择器策略(非常重要!)#

优先级选择器类型示例原因
1Role + namegetByRole("button", { name: "Submit" })可访问、用户可见
2LabelgetByLabel("Email address")可访问、语义化
3data-testidgetByTestId("checkout-form")稳定、测试专用
4Text contentgetByText("Welcome back")用户可见
CSS classes.btn-primary样式改了就挂
DOM 结构div > form > input:nth-child(2)任何重构都可能挂
// ❌ BAD: 脆弱的选择器
page.locator(".btn.btn-primary.submit-button").click();
page.locator("div > form > div:nth-child(2) > input").fill("text");
// ✅ GOOD: 稳定的选择器
page.getByRole("button", { name: "Submit" }).click();
page.getByLabel("Email address").fill("user@example.com");

4. 网络请求Mock#

隔离外部服务,让测试不依赖第三方:

test("shows error when API fails", async ({ page }) => {
await page.route("**/api/users", (route) => {
route.fulfill({
status: 500,
body: JSON.stringify({ error: "Server Error" }),
});
});
await page.goto("/users");
await expect(page.getByText("Failed to load users")).toBeVisible();
});

5. 视觉回归测试#

Playwright 原生支持截图对比:

test("homepage looks correct", async ({ page }) => {
await page.goto("/");
await expect(page).toHaveScreenshot("homepage.png", {
fullPage: true,
maxDiffPixels: 100, // 允许少量像素差异
});
});

6. 无障碍测试#

import AxeBuilder from "@axe-core/playwright";
test("page has no accessibility violations", async ({ page }) => {
const results = await new AxeBuilder({ page })
.exclude("#third-party-widget")
.analyze();
expect(results.violations).toEqual([]);
});

🛠️ CI/CD 集成示例#

# GitHub Actions
name: E2E Tests
on: [push, pull_request]
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run build
- run: npm run start & npx wait-on http://localhost:3000
- run: npx playwright test
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/

⚠️ 绝对不要做的事#

禁忌原因
❌ 用固定 waitForTimeout()导致 flaky 测试,跑得还慢
❌ 用 CSS class 或 DOM 结构做选择器样式/重构一改就挂
❌ 测试之间共享状态并行跑会打架
❌ 测试实现细节换个写法就 fail,毫无意义
❌ 不清理测试数据数据污染会导致后续测试 fail
❌ 所有东西都用 E2E 测太慢,用单元/集成测试覆盖边界情况
❌ 忽略 flaky 测试flaky 测试比没有测试还糟糕,马上修或删掉
❌ 在选择器里 hardcode 测试数据用动态等待处理变化的内容

🎯 总结#

E2E Testing Patterns 这个 Skill 简直就是前端测试的避坑指南喵!它教会我们:

  1. 测什么:聚焦关键用户路径,别什么都往 E2E 塞
  2. 怎么写:Page Object、Fixtures、Smart Waiting 这些模式让测试健壮又易维护
  3. 怎么选:role/label/testid > text > CSS class > DOM 结构
  4. 怎么集成:CI/CD 模板拿来就能用

如果你在做前端项目,强烈建议把这个 Skill 装下来好好研读一下~毕竟好的测试习惯,能让你睡个安稳觉,不用半夜被 bug 叫醒喵~ 🐱


安装命令:

Terminal window
npx clawhub@latest install e2e-testing-patterns
每日Skill学习 - E2E Testing Patterns
https://maomaoz.org/posts/daily-skill-2026-04-13/
Author
讨厌猫猫雨
Published at
2026-04-13
License
CC BY-NC-SA 4.0