Skip to content
返回质量保障

端到端测试

在类生产环境中驱动真实浏览器走完整用户旅程,从登录到结账。

为什么

端到端测试是唯一验证用户真能完成旅程的自动化——前端渲染、API 响应、数据库持久化,三者之间的接线成立。单元和集成测试各验一个切片,但部署可能断开前后端接缝(重命名的 API 字段、CORS 变更、路由不匹配),只有 E2E 测试能抓住。它们慢且 flaky,所以只写少量——但这少量守卫最高价值流程:注册、结账、赚钱的那个。

何时使用

只为前 3-5 个关键用户旅程(直接产生收入或入门必需的)写 E2E 测试。每条到达生产的大回归加一条。每次部署到 staging 时跑,每晚在类生产环境跑。

如何做

1. 选工具和环境
   - Playwright(现代、跨浏览器、自动等待)或 Cypress(开发者友好)
   - 对 staging 环境跑真实数据,非 localhost mock

2. 确定关键旅程(3-5 个,不要更多)
   - 注册 → 验证邮箱 → 首次登录 → 完成入门
   - 浏览商品 → 加购物车 → 结账 → 支付 → 确认
   - 80% 收入依赖的那个动作

3. 像用户一样写测试,而非像开发者探测
   - 点可见按钮、输入可见字段、等可见文本
   - 绝不用绕过 UI 的内部 CSS 类或 data-test ID
   - 仅当选择器否则有歧义时才加 data-testid

4. 处理异步和 flaky
   - 用自动等待(Playwright 等元素可操作)
   - 绝不用固定 sleep(sleep(2000));改为等条件
   - CI 中 flaky 测试最多重试 2 次,但任何需要重试的都要查

5. 在对的节奏跑
   - 每个 PR 若触及该旅程文件则跑(路径过滤门控)
   - 每晚全套件对最新 staging 部署跑
   - 绝不每次保存跑——太慢

示例

// e2e/checkout.spec.ts
import { test, expect } from "@playwright/test";

test("用户可以端到端购买商品", async ({ page }) => {
  // 登录
  await page.goto("/login");
  await page.getByLabel("Email").fill("[email protected]");
  await page.getByLabel("Password").fill("secret123");
  await page.getByRole("button", { name: "Sign in" }).click();
  await expect(page).toHaveURL("/dashboard");

  // 浏览并加购物车
  await page.getByRole("link", { name: "Wireless Headphones" }).click();
  await page.getByRole("button", { name: "Add to cart" }).click();
  await expect(page.getByText("1 item in cart")).toBeVisible();

  // 结账
  await page.getByRole("link", { name: "Cart" }).click();
  await page.getByRole("button", { name: "Checkout" }).click();
  await page.getByLabel("Card number").fill("4242424242424242");
  await page.getByLabel("Expiry").fill("12/28");
  await page.getByLabel("CVC").fill("123");
  await page.getByRole("button", { name: "Pay" }).click();

  // 确认
  await expect(page.getByText("Order confirmed")).toBeVisible({ timeout: 15000 });
  await expect(page).toHaveURL(/\/orders\/\d+/);
});

常见问题

  • ⚠用固定 sleep(await sleep(3000))等加载的 E2E 测试是 flaky 的——慢 CI 上加载要 3.5 秒测试就挂,快的上每条白等 3 秒,套件又慢又不可靠。
  • ⚠把每个小 UI 变体都写成 E2E 测试使套件膨胀到 200+ 条跑 40 分钟,团队不再在 PR 上跑,部署后才发现回归。
  • ⚠对 localhost 用 mock API 跑 E2E 全绿但漏掉所有真后端集成问题——测试证明前端和假实现工作,非和真系统。
  • ⚠绑 CSS 类或 DOM 结构的选择器(div > span:nth-child(2))在任何无害样式重构时挂,E2E 套件变成侵蚀信任的维护负担。

最佳实践

  • ✓用 Playwright 自动等待和 getByRole/getByLabel 选择器——它们扛得住样式变更且反映真实用户(或屏幕阅读器)如何寻址页面。
  • ✓E2E 套件保持在 20 条以内,只覆盖收入关键和入门旅程;其余下推到集成或单元测试。
  • ✓对 staging 环境用真数据库和真第三方沙箱(Stripe test mode、Mailtrap)跑 E2E,非 localhost mock——重点是测真实接线。
  • ✓每条 flaky E2E 测试 24 小时内查——被忽略的 flaky 测试比没测试更糟,因为它训练团队无视所有红信号。