如何用Vitest和Playwright搭建完整前端测试体系
前端测试体系的正确搭法是「两层分工」:Vitest 负责单元测试和组件测试,Playwright 负责端到端(E2E)测试,两者共用同一套 Vite 构建配置和 CI 流水线,覆盖率交给 Vitest 统计,真实浏览器行为交给 Playwright 验证。结论:不要试图用其中一个工具覆盖全部测试场景,分层是唯一能长期维护的方案。
Vitest 和 Playwright 到底有什么区别?
结论:Vitest 跑在 Node 里的模拟 DOM(jsdom/happy-dom)上,速度快但测不到真实浏览器行为;Playwright 驱动真实浏览器(Chromium/Firefox/WebKit),慢但能验证布局、网络、跳转。
Vitest 是构建在 Vite 之上的测试运行器,直接复用 vite.config.ts 的解析、别名、插件配置,冷启动通常在 1 秒内。Playwright 是微软开源的浏览器自动化框架,通过 CDP 等协议控制真实浏览器,支持多浏览器并行和 trace 录制。
具体分工建议按数量级划分:纯函数、hooks、状态管理层用 Vitest,占测试总量约 70%;组件渲染与交互用 Vitest + Testing Library,占 20%;登录、下单、支付这类跨页面关键链路用 Playwright,占 10%。
Vitest 怎么配置才不踩坑?
结论:把 test 配置写进 vite.config.ts,并显式设置 environment、setupFiles、coverage.thresholds 三项,其余用默认值即可。
安装命令:
npm i -D vitest @vitest/coverage-v8 jsdom @testing-library/react @testing-library/jest-dom
配置示例:
/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
export default defineConfig({
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./src/test/setup.ts'],
include: ['src/**/*.{test,spec}.{ts,tsx}'],
coverage: {
provider: 'v8',
reporter: ['text', 'lcov'],
thresholds: { lines: 80, functions: 80, branches: 70 }
}
}
})
注意两点:一,include 必须限定在 src/**,否则 Vitest 会误扫 e2e/ 目录下的 Playwright 用例并报错;二,thresholds 一旦设置,覆盖率不达标时 vitest run --coverage 会以非零码退出,CI 才能拦住。
Playwright 怎么配置才能稳定跑通?
结论:在 playwright.config.ts 里用 webServer 自动拉起开发服务器,CI 下开启 retries: 2 和 trace: 'on-first-retry',这是排查偶发失败的标准组合。
npm i -D @playwright/test
npx playwright install --with-deps
import { defineConfig, devices } from '@playwright/test'
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 2 : undefined,
use: { baseURL: 'http://localhost:5173', trace: 'on-first-retry' },
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
webServer: {
command: 'npm run dev',
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI
}
})
最大的坑是等待方式:禁止使用 page.waitForTimeout(3000) 这类固定等待,一律用 await expect(page.getByTestId('submit')).toBeVisible() 这类自动重试断言,Playwright 默认 5 秒超时、250ms 轮询,足够覆盖绝大多数异步渲染。
两套测试如何避免重复和冲突?
结论:用目录隔离 + 命名约定,src/**/*.test.tsx 归 Vitest,e2e/*.spec.ts 归 Playwright,互不越界。
在 package.json 里分开脚本:
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"test:cov": "vitest run --coverage",
"e2e": "playwright test",
"e2e:ui": "playwright test --ui"
}
}
选择器策略也要统一:单元和组件测试用 getByRole 优先,E2E 用 data-testid 兜底,避免测试绑死在 CSS 类名上。同一业务逻辑不要在两层各写一遍——组件测试验证「点击后调用了一次 API」,E2E 只验证「点击后页面上出现了订单号」。
CI 里怎么把两者串起来?
结论:GitHub Actions 里拆成两个 job 并行执行,单元测试 job 跑 npm run test:cov,E2E job 跑 npx playwright test --shard=1/2,总时长可从串行的 12 分钟压到 5 分钟左右。
E2E job 必须加上失败时上传 playwright-report/ 的步骤,否则 trace 文件拿不到,线上偶发失败无法复盘。另外 Playwright 官方镜像 mcr.microsoft.com/playwright:v1.4x-jammy 已预装浏览器依赖,比 npx playwright install --with-deps 快约 40 秒。
收束一下:Vitest 管住 90% 的逻辑正确性,Playwright 管住 10% 的真实链路,配置上守住「目录隔离、覆盖率阈值、禁用固定等待、CI 并行 + trace 上传」这四条,这套体系就能在团队里活过半年而不被废弃。
原文链接:https://www.gj0.com/thread-221.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。