--- id: frontend.fe-testing-perf.testing title: 前端测试:Vitest 单元测试、Testing Library 组件测试、Playwright 端到端测试 type: lesson domain: frontend stack: [fe-testing-perf] tags: [Vitest, Testing Library, Playwright, 假定时器, Mock, 竞态, 测试金字塔] level: intermediate prerequisites: [frontend.react.hooks-rendering] est_hours: 2.5 related: [frontend.fe-testing-perf.web-vitals, frontend.fe-testing-perf.interview, ai.ai-coding.verify-evidence] week: [7] order: 1 --- # 前端测试:Vitest 单元测试、Testing Library 组件测试、Playwright 端到端测试 > BOSS直聘 2026-10-23 的调研中,多个岗位要求“用 AI 写代码并保证质量”:测试是证明 AI 生成代码正确的最直接证据(见 [AI 编程:验证与证据](../../ai/ai-coding/02-verify-evidence.md))。工具版本以官方文档为准:https://vitest.dev/ 、https://testing-library.com/ 、https://playwright.dev/ 。 ## 解释说明 ### 测试金字塔 | 层级 | 工具 | 测什么 | 数量 | | --- | --- | --- | --- | | 单元测试 | Vitest | 纯函数、状态机、工具函数、组合式函数 / Hooks | 最多 | | 组件测试 | Vitest + Testing Library(jsdom) | 组件在用户操作下的表现 | 适中 | | 端到端测试 | Playwright | 真实浏览器中的关键业务流程 | 少而精 | ### Testing Library 的原则 **按用户的方式测试**:通过角色、标签文字查找元素(`getByRole`、`getByLabelText`),而不是 CSS 类名或组件内部状态;断言用户能看到的结果。好处是重构实现时测试不用改,同时促使组件具备可访问性。 ### 常用技巧 - **假定时器**:`vi.useFakeTimers()` + `vi.advanceTimersByTimeAsync()` 测试防抖、轮询、超时,不需要真的等待。 - **Mock**:`vi.fn()` 模拟依赖(接口请求函数作为参数注入,测试时传入假的实现);端到端测试中用 `page.route` 拦截接口返回固定数据。 - **测竞态**:让旧请求比新请求慢,断言最终显示新结果。 - **端到端测试稳定性**:使用自动等待的断言(`await expect(locator).toHaveCount(2)`),不要写固定的 `sleep`。 ## 案例 订单搜索框:防抖 300ms;输入变化时中止旧请求;失败显示错误。三个测试分别覆盖防抖、竞态和失败;把组件中的 `controller.abort()` 删除后,竞态测试会失败——证明测试真的在保护这个行为。 ## 代码示例 完整示例:[examples/search-box](examples/search-box/)(`vitest run`:3 个测试通过;`tsc` 通过;删除中止逻辑后竞态测试失败,已验证)。 ```tsx it("竞态:慢的旧请求晚返回,不会覆盖新结果", async () => { const search: SearchFn = (q, signal) => new Promise((resolve) => { const ms = q === "old" ? 1000 : 100; const t = setTimeout(() => resolve([`结果-${q}`]), ms); signal.addEventListener("abort", () => clearTimeout(t)); }); render(); typeInto("old"); await act(() => vi.advanceTimersByTimeAsync(300)); typeInto("new"); await act(() => vi.advanceTimersByTimeAsync(2000)); expect(screen.getByRole("list", { name: "搜索结果" }).textContent).toBe("结果-new"); }); ``` Playwright 端到端测试(示意,未在浏览器中实际运行):[e2e/search.spec.ts](examples/search-box/e2e/search.spec.ts)。 ## 易错点 - 测试实现细节(组件内部 state、私有方法),重构就挂。 - 用真实定时器等待,测试又慢又不稳定。 - 组件内部直接 `fetch`,测试只能 mock 全局对象:把依赖作为参数或通过上下文注入更好测。 - 端到端测试依赖测试环境的真实数据,数据一变就失败。 - 只追求覆盖率数字:没有断言的测试也能提高覆盖率。 ## 面试常问 - 你们项目怎么做前端测试?测试金字塔每层测什么? - 怎么测试防抖、定时器、异步请求? - 端到端测试不稳定怎么办?