Web 前端手册WEB FRONTEND

CHAPTER 07生产层 · TESTING

测试与质量

Vitest/Jest、Testing Library、Playwright E2E 与覆盖率。让重构与迭代有安全网。

开始阅读难度中级先修第四、六章约2小时
07.1

测试金字塔

大量单元测试(快、隔离)、适量组件测试、少量 E2E(覆盖关键路径)。过多 E2E 会慢且脆弱。

测行为不测实现

断言"用户能看到什么、点了发生什么",而非内部 state 值。实现改了测试也不该崩。

07.2

Vitest 单测

Vitest 与 Vite 同源、API 兼容 Jest,启动快。用 describe/it/expect 写纯函数测试。

import { describe, it, expect } from 'vitest';
import { sum } from './math';
describe('sum', () => {
  it('加两个正数', () => expect(sum(1, 2)).toBe(3));
});
07.3

Testing Library

Testing Library 鼓励"像用户一样查询 DOM":按角色、文本、标签查找元素,而非依赖测试 id 或内部类名。

import { render, screen } from '@testing-library/react';
render(<Greeting name="A" />);
expect(screen.getByText('Hello A')).toBeInTheDocument();
查询优先级

优先 getByRole(无障碍友好)> getByLabelText > getByText > getByTestId(最后手段)。

07.4

组件交互

用 fireEvent 或 userEvent 模拟点击、输入,再断言 UI 变化,覆盖真实交互路径。

const btn = screen.getByRole('button', { name: '加一' });
await userEvent.click(btn);
expect(screen.getByText('1')).toBeInTheDocument();
07.5

Mock 网络

组件测试不应真发请求。用 MSW(Mock Service Worker)或 vi.mock 拦截 fetch,返回固定数据,保证确定与速度。

import { http, HttpResponse } from 'msw';
const handlers = [
  http.get('/api/user', () => HttpResponse.json({ name: 'A' }))
];
07.6

Playwright E2E

Playwright 驱动真实浏览器跑完整用户流程(登录→下单),最接近线上行为,用于守护关键链路。

import { test, expect } from '@playwright/test';
test('登录后可看到主页', async ({ page }) => {
  await page.goto('/login');
  await page.fill('#email', 'a@b.com');
  await page.click('button[type=submit]');
  await expect(page).toHaveURL('/home');
});
E2E 别贪多

E2E 慢且易碎,只覆盖核心业务流(登录、下单、支付)。逻辑细节交给单测与组件测试。

07.7

覆盖率

Vitest 内置覆盖率(基于 V8)。关注关键路径分支,而非盲目追求 100%。

vitest run --coverage
07.8

常见陷阱与 FAQ

测了实现细节导致测试很脆?

改用 Testing Library 按用户视角查询,断言可见结果。组件内部状态变化不该是断言对象。

组件测试真发请求怎么办?

用 MSW 在测试环境拦截网络,返回 mock 数据,既快又确定,还能模拟错误场景。

E2E 总不稳定?

避免依赖具体等待时间,用 waitFor/断言可见性做显式等待;用测试专用账号与数据,隔离环境。

07.9

实战练习与资源

延伸资源