前端单元测试入门:使用 Vitest + Vue 测试组件逻辑与交互
一、前言
你是否经历过:
“改了一个小功能,结果不小心把其他模块搞崩了,测试才发现。”
随着前端项目日益复杂,手动回归测试成本越来越高。自动化测试成为保障代码质量、提升开发效率的必备手段。
本文将带你使用 Vitest(新一代前端测试框架)对 Vue 3 组件进行单元测试,涵盖:
✅ 组件渲染测试
✅ 事件触发与逻辑验证
✅ 模拟接口调用(Mock)
✅ 测试覆盖率报告
无需测试经验,零基础也能上手!
二、为什么选择 Vitest?
特性 | 说明 |
---|---|
⚡ 极速运行 | 基于 Vite,启动快、热更新快 |
📦 天然集成 | 与 Vite 项目无缝衔接,无需额外配置 |
🧪 兼容 Jest API | 大部分 Jest 语法可直接使用 |
📊 内置覆盖率 | 支持 --coverage 自动生成报告 |
🌿 轻量现代 | 专为 ES Module 设计,适合 Vue/React 项目 |
✅ Vitest 是当前 Vue 生态中最推荐的测试方案之一。
三、环境搭建
1. 创建 Vue 3 项目(如未创建)
npm create vue@latest my-vue-app
# 选择:TypeScript, Vue Router, Vitest 等
cd my-vue-app
npm install
2. 确保已安装 Vitest
npm install --save-dev vitest @testing-library/vue
✅
@testing-library/vue
是 Vue 官方推荐的测试工具库,用于渲染组件、触发事件等。
四、编写第一个测试用例
1. 创建待测组件
<!-- components/Counter.vue -->
<template><div class="counter"><p>计数: {{ count }}</p><button @click="increment">+1</button><button @click="decrement">-1</button></div>
</template><script setup>
import { ref } from 'vue'const count = ref(0)function increment() {count.value++
}function decrement() {count.value--
}
</script>
2. 创建测试文件
// tests/unit/Counter.test.js
import { render, screen, fireEvent } from '@testing-library/vue'
import Counter from '../../src/components/Counter.vue'describe('Counter 组件', () => {test('初始显示计数为 0', () => {render(Counter)// 断言:页面包含 "计数: 0"expect(screen.getByText('计数: 0')).toBeInTheDocument()})test('点击 +1 按钮,计数加 1', async () => {render(Counter)const button = screen.getByText('+1')// 模拟点击await fireEvent.click(button)// 断言:计数变为 1expect(screen.getByText('计数: 1')).toBeInTheDocument()})test('点击 -1 按钮,计数减 1', async () => {render(Counter)const button = screen.getByText('-1')await fireEvent.click(button)expect(screen.getByText('计数: -1')).toBeInTheDocument()})
})
五、运行测试
1. 添加 npm 脚本
// package.json
{"scripts": {"test": "vitest","test:coverage": "vitest --coverage"}
}
2. 运行测试
npm run test
✅ 输出:
✓ tests/unit/Counter.test.js (3 tests) [1.23s]Test Files 1 passed (1)Tests 3 passed (3)Start at 10:00:00Duration 1.52s
六、进阶测试:模拟接口调用(Mock)
1. 创建异步组件
<!-- components/UserProfile.vue -->
<template><div><p v-if="loading">加载中...</p><p v-else-if="user">{{ user.name }}</p><p v-else>用户不存在</p></div>
</template><script setup>
import { ref, onMounted } from 'vue'const user = ref(null)
const loading = ref(true)async function fetchUser() {const res = await fetch('/api/user/1')user.value = await res.json()loading.value = false
}onMounted(() => {fetchUser()
})
</script>
2. 测试异步逻辑(使用 Mock)
// tests/unit/UserProfile.test.js
import { render, screen } from '@testing-library/vue'
import UserProfile from '../../src/components/UserProfile.vue'
import { vi } from 'vitest'// 模拟 fetch
global.fetch = vi.fn()describe('UserProfile 组件', () => {afterEach(() => {vi.clearAllMocks()})test('加载时显示“加载中”', () => {render(UserProfile)expect(screen.getByText('加载中...')).toBeInTheDocument()})test('获取用户成功,显示用户名', async () => {fetch.mockResolvedValueOnce({json: async () => ({ id: 1, name: '张三' })})render(UserProfile)// 等待异步操作完成await screen.findByText('张三')expect(screen.getByText('张三')).toBeInTheDocument()})
})
✅ 使用
vi.fn()
模拟fetch
,避免真实网络请求。
七、生成测试覆盖率报告
npm run test:coverage
生成 coverage/
目录,打开 index.html
可查看:
✅ 哪些代码被测试覆盖
❌ 哪些代码未被覆盖(红色标记)
💡 建议:关键业务逻辑覆盖率应 > 80%。
八、最佳实践
实践 | 说明 |
---|---|
测试命名清晰 | 使用 describe 和 test 描述行为 |
测试独立 | 每个测试用例独立,不依赖其他测试 |
Mock 外部依赖 | 避免网络、数据库等外部调用 |
覆盖边界情况 | 如空状态、错误处理 |
集成 CI/CD | 提交代码时自动运行测试 |
九、总结
通过 Vitest + Vue Testing Library,你可以:
✅ 快速为 Vue 组件编写单元测试。
✅ 自动验证组件渲染、事件、异步逻辑。
✅ 提升代码质量,减少回归 bug。
✅ 建立可持续的测试文化。