出处:掘金
原作者:金泽宸
前端测试体系不是“测试团队的事”,而是让架构可持续、业务可验证、上线有信心的核心保障
写在前面
架构师不是只管搭框架,更要构建一套“可被验证、可被信任”的测试体系
很多团队的测试现状是这样的:
- 测试都靠点一遍
- 上线后才发现按钮没绑事件
- 重构组件后多个页面挂了没人发现
- PR 代码合并了,线上崩了
解决这些问题,必须搭建一个可落地、可自动化、覆盖从组件到页面的测试体系
测试金字塔结构(前端视角)
类型 | 说明 | 工具推荐 | 目的 |
---|---|---|---|
单元测试 | 函数、组件渲染、交互逻辑 | Vitest / Jest | 测组件、函数 |
集成测试 | 组件组合、状态流、API 调用 | @testing-library/vue | 组件交互联动 |
E2E 测试 | 覆盖用户行为流,发现集成级 bug | Cypress / Playwright | 模拟真实用户操作 |
技术选型建议(Vue 项目)
模块 | 工具 |
---|---|
测试框架 | Vitest(更快、更现代) |
渲染测试 | @testing-library/vue |
模拟请求 | msw(Mock Service Worker) |
端到端测试 | Cypress / Playwright |
Mock 工具 | vi.fn / sinon / faker |
组件单元测试示例
以 UserCard.vue
为例:
<template>
<div class="user-card">
<h3>{{ user.name }}</h3>
<button @click="onFollow">关注</button>
</div>
</template>
<script setup>
const props = defineProps({ user: Object, onFollow: Function })
</script>
单测:
import { render, fireEvent } from '@testing-library/vue'
import UserCard from './UserCard.vue'
test('渲染用户名 + 点击关注', async () => {
const user = { name: '张三' }
const fn = vi.fn()
const { getByText } = render(UserCard, {
props: { user, onFollow: fn },
})
getByText('张三')
await fireEvent.click(getByText('关注'))
expect(fn).toHaveBeenCalled()
})
集成测试示例
以表单组合为例(组件交互 & 状态联动):
<template>
<Form @submit="handle">
<Input v-model="name" />
<Button type="submit">提交</Button>
</Form>
</template>
集成测试:
test('输入后提交按钮触发', async () => {
const { getByRole } = render(MyForm)
const input = getByRole('textbox')
const btn = getByRole('button')
await fireEvent.update(input, 'hello')
await fireEvent.click(btn)
// 断言提交行为
})
E2E 测试流程(Cypress)
安装 Cypress:
npm install -D cypress
npx cypress open
示例测试文件:
describe('登录流程', () => {
it('输入用户名密码并跳转首页', () => {
cy.visit('/login')
cy.get('#username').type('admin')
cy.get('#password').type('123456')
cy.get('button').click()
cy.url().should('include', '/home')
})
})
E2E 自动化优势:
- 真浏览器环境 + 网络模拟
- 多端适配验证
- 快照 + 录像记录(CI/CD 用)
Mock 请求工具(MSW)
// mocks/handlers.ts
import { rest } from 'msw'
export const handlers = [
rest.get('/api/user', (req, res, ctx) => {
return res(ctx.json({ name: '张三' }))
}),
]
配合组件测试:
import { setupServer } from 'msw/node'
import { handlers } from './handlers'
const server = setupServer(...handlers)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
测试目录与命名规范建议
src/
├── components/
│ └── UserCard.vue
│ └── __tests__/
│ └── UserCard.test.ts
├── pages/
│ └── Home.vue
├── tests/
│ ├── e2e/
│ │ └── login.spec.ts
│ ├── mocks/
│ │ └── handlers.ts
建议命名:组件名.test.ts
或 组件名.spec.ts
测试集成进 CI/CD
在 GitHub Actions 添加:
- name: 🧪 运行单元测试
run: npm run test
- name: 🧪 运行 E2E 测试(Cypress)
uses: cypress-io/github-action@v5
with:
start: npm run dev
wait-on: 'http://localhost:5173'
成功后打包部署,失败自动阻断发布流程
测试覆盖范围建议
类型 | 推荐覆盖点 |
---|---|
函数工具类 | 所有核心逻辑分支 |
UI 组件 | 展示正确、交互逻辑、边界行为 |
复合组件 | 状态联动、异常状态、表单校验 |
页面流程 | 登陆、跳转、错误提示、弹窗流程 |
权限/路由 | 不同用户是否看到按钮、页面 |
测试文化落地建议
目标 | 落地方式 |
---|---|
强制新组件带测试 | 新建组件 CLI 自动生成 test 文件 |
每次构建检查覆盖率 | 使用 vitest --coverage ,CI 中设最低阈值 |
开发阶段自动执行 | 使用 vitest --watch 实时反馈 |
拉新 PR 自动测试 | Actions + 报告输出(测试截图、失败栈) |
低风险回归 | 所有核心模块都具备覆盖率 70%+ |