人人都会AI编程

19.2 集成测试与 E2E 测试:Cypress / Playwright

更新时间:2026-07-10

单元测试能保证单个组件或函数的正确性,但真实的应用是由多个模块协同工作的。当组件相互组合、路由跳转、API 数据真实返回时,集成测试和端到端(E2E)测试就变得不可或缺。它们从用户的角度出发,验证完整的业务流程是否正常运行。

集成测试 vs E2E 测试

  • 集成测试:验证几个模块组合在一起是否正常工作。在 React 中,通常表现为测试一个页面级组件,模拟 API 返回、用户点击、状态变化,但不会启动真实的后端服务或完整浏览器环境。仍可以使用 Jest + React Testing Library,只是测试范围更大。
  • E2E 测试:完全模拟真实用户操作,启动真实浏览器(或 headless 模式),访问完整应用(包括前端、后端、数据库),验证从打开页面到完成一笔交易的整个流程。E2E 测试最接近用户真实体验,但运行慢、维护成本高。

良好的测试金字塔告诉我们:单元测试多、集成测试适中、E2E 测试少。Cypress 和 Playwright 是当前最主流的 E2E 测试框架,它们也可以用来编写集成测试(配合 mock 后端)。

Cypress:开发者友好的 E2E 测试框架

Cypress 是一个基于 JavaScript 的端到端测试工具,专为现代 Web 应用设计。它的核心特色是运行在浏览器内,可以直接访问 DOM、网络请求、本地存储等,调试体验极佳。

核心能力:

  • 时间旅行:测试运行时会自动截图,你可以回放每一步操作时的界面状态。
  • 自动等待:查询元素时自动重试,无需写大量 sleepwait
  • 网络请求控制:支持拦截和修改 API 响应(cy.intercept),方便模拟各种接口场景。
  • 实时重载:改动测试代码后自动重新运行,开发体验接近前端热更新。
  • 调试直观:在 Cypress 的电子界面里可以看到浏览器控制台、网络日志、DOM 快照。

安装与基础使用:

npm install cypress --save-dev
npx cypress open

第一次运行会生成 cypress 目录,包含配置文件和示例测试。一个典型的 React 应用登录测试如下:

// cypress/e2e/login.cy.js
describe('登录流程', () => {
  it('应该成功登录并跳转到主页', () => {
    cy.visit('http://localhost:3000/login');
    cy.get('input[name="username"]').type('testuser');
    cy.get('input[name="password"]').type('password123');
    cy.get('button[type="submit"]').click();

    // 断言跳转到主页
    cy.url().should('include', '/dashboard');
    cy.contains('欢迎回来,testuser').should('be.visible');
  });

  it('登录失败时应显示错误提示', () => {
    cy.intercept('POST', '/api/login', {
      statusCode: 401,
      body: { message: '用户名或密码错误' }
    }).as('loginAttempt');

    cy.visit('/login');
    cy.get('input[name="username"]').type('wrong');
    cy.get('input[name="password"]').type('wrong');
    cy.get('button[type="submit"]').click();
    cy.wait('@loginAttempt');
    cy.contains('用户名或密码错误').should('exist');
  });
});

实用技巧:

  • 使用 cy.intercept 模拟 API 响应,可模拟正常、延迟、失败等场景,无需启动真实后端。
  • 将登录等通用前置操作封装为自定义命令 Cypress.Commands.add('login', () => {...}),减少重复代码。
  • 对于需要多标签页或跨域的场景有所限制(Cypress 单标签、同源),复杂场景可考虑 Playwright。

Playwright:微软出品的跨浏览器自动化利器

Playwright 是一个由微软开发的端到端测试框架,支持 Chromium、Firefox、WebKit 三种浏览器引擎。你可以用同一套脚本在多种浏览器上并行测试,覆盖 Safari 等 webkit 内核浏览器,这是 Cypress 目前不直接支持的。

核心能力:

  • 跨浏览器支持:一份代码跑在 Chrome、Edge、Firefox、Safari。
  • 自动等待:和 Cypress 类似的智能等待机制,避免 flaky 测试。
  • 网络拦截与控制:可修改请求/响应,模拟网络异常、文件上传下载等。
  • 强大的选择器引擎:支持文本选择器、角色选择器、CSS/XPath,并能自动生成选择器。
  • 多标签页、多窗口、iframe 支持:天生支持复杂场景,如第三方登录窗口。
  • 测试生成器npx playwright codegen 可录制操作生成测试代码,快速上手。
  • Trace Viewer:记录完整的测试运行过程,包含 DOM 快照、网络请求、控制台日志,用于事后分析失败原因。

安装与基础使用:

npm init playwright@latest

该命令会引导你安装浏览器驱动、创建配置文件。一个完全相同的登录测试用 Playwright 写出来是这样的:

// tests/login.spec.js
const { test, expect } = require('@playwright/test');

test.describe('登录流程', () => {
  test('应该成功登录并跳转到主页', async ({ page }) => {
    await page.goto('http://localhost:3000/login');
    await page.fill('input[name="username"]', 'testuser');
    await page.fill('input[name="password"]', 'password123');
    await page.click('button[type="submit"]');

    // 等待导航完成
    await expect(page).toHaveURL(/.*dashboard/);
    await expect(page.locator('text=欢迎回来,testuser')).toBeVisible();
  });

  test('登录失败时应显示错误提示', async ({ page }) => {
    // 拦截登录请求并返回失败
    await page.route('**/api/login', route => {
      route.fulfill({
        status: 401,
        body: JSON.stringify({ message: '用户名或密码错误' })
      });
    });

    await page.goto('/login');
    await page.fill('input[name="username"]', 'wrong');
    await page.fill('input[name="password"]', 'wrong');
    await page.click('button[type="submit"]');

    await expect(page.locator('text=用户名或密码错误')).toBeVisible();
  });
});

实用技巧:

  • 使用 page.route 进行网络拦截,比 Cypress intercept 语法稍复杂但功能更强大。
  • 利用 test.use({ storageState: 'auth.json' }) 可以保存和复用登录态,避免每次测试都登录。
  • 在多浏览器上并行测试:npx playwright test --browser=all
  • 生成可视化报告:npx playwright show-report,查看 Trace 分析失败原因。

Cypress vs Playwright:选型对比

| 特性 | Cypress | Playwright |
|------|--------|------------|
| 浏览器支持 | Chrome、Firefox、Edge(Chromium内核) | Chrome、Firefox、Safari(WebKit)全兼容 |
| 调试体验 | 最强,时间旅行、实时热重启 | 较好,Trace Viewer 功能强大 |
| 多标签/窗口 | 不支持 | 原生支持 |
| 多浏览器并行 | 通过插件或付费仪表板 | 内置支持 |
| 网络拦截 | cy.intercept 简洁直观 | page.route 更底层灵活 |
| 社区生态 | 非常成熟,插件丰富 | 快速增长,微软背书 |
| 性能 | 中等(串行执行) | 更快,原生并行 |
| 学习曲线 | 低,API 语义化 | 中等,Playwright 需要理解异步等待 |

建议:

  • 如果你的团队已经使用 Cypress,且不需要跨浏览器测试或复杂多标签页场景,Cypress 的开发体验仍然是一流的。
  • 新项目、需要覆盖 Safari 或其他浏览器、或者需要多标签页的场景,优先选择 Playwright。
  • 也可以两者共用:Cypress 用于开发阶段的快速反馈,Playwright 用于 CI/CD 中进行多浏览器验收测试。

测试的实用原则

  1. 从用户路径出发:不要测试代码实现的细节,而是模拟真实操作步骤。
  2. 尽量使用稳定的选择器:优先使用 data-testid 属性而不是 CSS class 或文本,避免 UI 调整导致测试失效。
  3. 控制测试环境:通过环境变量或 fixture 生成一致的测试数据,避免依赖外部不稳定服务。
  4. 只测试关键流程:E2E 测试成本高,聚焦核心业务路径(注册、登录、下单、支付),边缘情况留给单元测试和集成测试。
  5. CI 集成:在 GitHub Actions / GitLab CI 等流水线中运行 E2E 测试,保证合并前的质量门禁。
# GitHub Actions 中使用 Playwright 示例
- name: Run E2E tests
  uses: microsoft/playwright-github-action@v1
  with:
    command: npx playwright test

结合 React 项目的实战建议

  • 集成测试:在 React 中可以使用 React Testing Library 渲染整个页面组件,并 mock 掉 API 请求(例如用 MSW),这属于集成测试范畴,运行速度快,可以验证 UI 与数据流的连接。
  • E2E 测试:启动开发服务器(npm run dev)后,用 Cypress 或 Playwright 访问该 URL。通常会在 CI 中先构建生产版本,然后启动静态服务器运行测试。

无论选择哪个工具,将 E2E 测试加入到你的开发流程中,都能大幅降低回归风险,让你有信心频繁部署。它们是质量保障的最后一道防线,让“上线不慌”成为现实。