前端测试怎样分层?Jest、Vitest、Testing Library、Playwright 各负责什么?

先理解原理和示例,再练习完整口述与追问。

单元测试验证纯函数和独立模块;组件测试用 Testing Library 从用户视角查询元素、触发交互;集成测试用 MSW 等模拟接口验证模块协作;端到端测试用 Playwright 或 Cypress 在真实浏览器跑关键流程。Jest 与 Vitest 是测试运行器,Vite 项目优先 Vitest。比例上底层多、E2E 少而精。

测试金字塔或“测试奖杯”的共同观点是:越接近用户越可信,但越慢越脆弱。Testing Library 鼓励按角色和文字查询(getByRole('button', { name: '提交' })),而不是依赖类名或内部状态,这样重构实现不会导致测试失败,也顺带检查了可访问性。

Vitest 复用 Vite 的转换管线与配置,原生支持 ESM 和 TypeScript,API 与 Jest 基本兼容。Playwright 自动等待元素可交互,支持多浏览器、并行、录制与 trace 回放,适合登录、下单、支付等核心路径。快照测试只适合稳定的小片段,整页快照往往变成无人审阅的“更新快照”。

js
import { render, screen } from '@testing-library/vue'
import userEvent from '@testing-library/user-event'
import LoginForm from './LoginForm.vue'

test('密码为空时提示错误', async () => {
  render(LoginForm)
  await userEvent.type(screen.getByLabelText('用户名'), 'ada')
  await userEvent.click(screen.getByRole('button', { name: '登录' }))
  expect(await screen.findByText('请输入密码')).toBeTruthy()
})

追求覆盖率数字会催生只执行代码、不做断言的测试;测试之间共享状态或依赖执行顺序会导致偶发失败。E2E 中用固定 sleep 等待会让测试又慢又不稳定。

我按风险分层:纯函数和工具类写单元测试,组件用 Testing Library 按用户可见的角色和文字去查询、交互,接口用 MSW 模拟做集成测试,核心业务流程用 Playwright 在真实浏览器跑 E2E。运行器 Vite 项目用 Vitest,老项目用 Jest。比例上单元和组件测试多,E2E 少而精。不追求覆盖率数字,而是保证关键路径和容易出错的边界被覆盖,并在 CI 中运行。