一、前言
你是否经历过:
"改了一个小功能,结果不小心把其他模块搞崩了,测试才发现。"
随着前端项目日益复杂,手动回归测试 成本越来越高。自动化测试成为保障代码质量、提升开发效率的必备手段。
本文将带你使用 Vitest (新一代前端测试框架)对 Vue 3 组件进行单元测试,涵盖:
- ✅ 组件渲染测试
- ✅ 事件触发与逻辑验证
- ✅ 模拟接口调用(Mock)
- ✅ 测试覆盖率报告
无需测试经验,零基础也能上手!
二、为什么选择 Vitest?
特性 | 说明 |
---|---|
⚡ 极速运行 | 基于 Vite,启动快、热更新快 |
📦 天然集成 | 与 Vite 项目无缝衔接,无需额外配置 |
🧪 兼容 Jest API | 大部分 Jest 语法可直接使用 |
📊 内置覆盖率 | 支持 --coverage 自动生成报告 |
🌿 轻量现代 | 专为 ES Module 设计,适合 Vue/React 项目 |
✅ Vitest 是当前 Vue 生态中最推荐的测试方案之一。
三、环境搭建
1. 创建 Vue 3 项目(如未创建)
perl
npm create vue@latest my-vue-app
# 选择:TypeScript, Vue Router, Vitest 等
cd my-vue-app
npm install
2. 确保已安装 Vitest
css
npm install --save-dev vitest @testing-library/vue
✅
@testing-library/vue
是 Vue 官方推荐的测试工具库,用于渲染组件、触发事件等。
四、编写第一个测试用例
1. 创建待测组件
xml
<!-- 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. 创建测试文件
javascript
// 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)
// 断言:计数变为 1
expect(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 脚本
json
// package.json
{
"scripts": {
"test": "vitest",
"test:coverage": "vitest --coverage"
}
}
2. 运行测试
arduino
npm run test
✅ 输出:
scss
✓ tests/unit/Counter.test.js (3 tests) [1.23s]
Test Files 1 passed (1)
Tests 3 passed (3)
Start at 10:00:00
Duration 1.52s
六、进阶测试:模拟接口调用(Mock)
1. 创建异步组件
xml
<!-- 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)
javascript
// 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
,避免真实网络请求。
七、生成测试覆盖率报告
arduino
npm run test:coverage
生成 coverage/
目录,打开 index.html
可查看:
- ✅ 哪些代码被测试覆盖
- ❌ 哪些代码未被覆盖(红色标记)
💡 建议:关键业务逻辑覆盖率应 > 80%。
八、最佳实践
实践 | 说明 |
---|---|
测试命名清晰 | 使用 describe 和 test 描述行为 |
测试独立 | 每个测试用例独立,不依赖其他测试 |
Mock 外部依赖 | 避免网络、数据库等外部调用 |
覆盖边界情况 | 如空状态、错误处理 |
集成 CI/CD | 提交代码时自动运行测试 |
九、总结
通过 Vitest + Vue Testing Library,你可以:
- ✅ 快速为 Vue 组件编写单元测试。
- ✅ 自动验证组件渲染、事件、异步逻辑。
- ✅ 提升代码质量,减少回归 bug。
- ✅ 建立可持续的测试文化。