在 Vue 3 + Vite 生态中,Vitest 是绝对的首选,因为它与 Vite 共享配置,速度极快,且 API 与 Jest 高度兼容。
下面是 Vitest 的完整接入指南,涵盖了从「环境配置」到「实战测试(含 Vue 组件测试)」的全流程。
一、 核心配置步骤
1. 安装核心依赖
除了测试框架本身,还需要安装 Vue 组件测试工具和 JSDOM(提供浏览器环境):
pnpmadd-Dvitest @vue/test-utils jsdom2. 配置vite.config.ts
在现有的 Vite 配置中追加test字段:
// vite.config.tsimport{defineConfig}from'vite'importvuefrom'@vitejs/plugin-vue'exportdefaultdefineConfig({plugins:[vue()],test:{environment:'jsdom',// 模拟浏览器 DOM 环境globals:true,// 全局注入 describe, it, expect 等,无需手动 import// 自动清理 mock 状态,避免测试用例间相互污染clearMocks:true,},})3. 补充 TS 类型提示
在tsconfig.app.json中加入vitest/globals,让编辑器认识describe、expect:
{"compilerOptions":{"types":["vitest/globals"]}}4. 添加测试脚本
在package.json中配置运行命令:
{"scripts":{"test":"vitest","test:run":"vitest run"}}二、 完整实战示例
示例 1:测试普通工具函数(纯逻辑)
假设我们有一个简单的格式化函数:
// src/utils/format.tsexportfunctionformatPrice(price:number):string{return`¥${price.toFixed(2)}`}对应的测试文件(建议放在同级目录或__tests__目录下):
// src/utils/__tests__/format.test.tsimport{formatPrice}from'../format'describe('formatPrice',()=>{it('应该正确格式化价格',()=>{expect(formatPrice(100)).toBe('¥100.00')expect(formatPrice(9.9)).toBe('¥9.90')})})示例 2:测试 Vue 组件(结合 @vue/test-utils)
假设我们有一个简单的按钮组件:
<!-- src/components/BaseButton.vue --> <script setup lang="ts"> const emit = defineEmits(['click']) </script> <template> <button class="base-btn" @click="emit('click')"> <slot /> </button> </template>组件的测试用例:
// src/components/__tests__/BaseButton.test.tsimport{mount}from'@vue/test-utils'importBaseButtonfrom'../BaseButton.vue'describe('BaseButton',()=>{it('应该正确渲染插槽内容',()=>{constwrapper=mount(BaseButton,{slots:{default:'点击我'}})expect(wrapper.text()).toContain('点击我')})it('点击时应该触发 click 事件',async()=>{constwrapper=mount(BaseButton)awaitwrapper.trigger('click')expect(wrapper.emitted('click')).toHaveLength(1)})})示例 3:测试 Pinia Store(结合权限路由场景)
测试我们之前写的usePermissionStore:
// src/stores/__tests__/permission.test.tsimport{setActivePinia,createPinia}from'pinia'import{usePermissionStore}from'../modules/permission'describe('Permission Store',()=>{beforeEach(()=>{setActivePinia(createPinia())})it('admin 角色应该能看到所有路由',()=>{conststore=usePermissionStore()constroutes=store.generateRoutes(['admin'])// 验证 admin 能获取到包含 system 的路由expect(routes.some(r=>r.path==='/system')).toBe(true)})it('普通用户应该被过滤掉无权限路由',()=>{conststore=usePermissionStore()constroutes=store.generateRoutes(['user'])expect(routes.some(r=>r.path==='/system')).toBe(false)})})三、 结合 Husky 的自动化流程
为了让测试真正发挥“守住代码质量”的作用,我们将测试加入 Git 提交拦截中:
pnpmadd-Dlint-staged修改package.json:
{"lint-staged":{"*.{ts,vue}":["vitest related --run","eslint --fix","prettier --write"]}}💡 亮点:vitest related是 Vitest 的神级功能!它只会运行与你本次修改的文件有关联的测试,而不是全量跑测试,这样在git commit时速度会非常快。
四、 避坑与最佳实践
- 测试文件命名:推荐统一使用
xxx.test.ts或xxx.spec.ts,Vitest 默认会匹配这些后缀。 - 异步测试:如果测试涉及接口请求或定时器,记得使用
async/await,或者在it回调中返回 Promise。 - Mock 外部依赖:在测试组件时,如果组件内部调用了
axios或router,使用vi.mock()进行模拟,避免测试变成真实的网络请求: - UI 可视化界面:开发时可以直接运行
pnpm test,Vitest 会启动一个 HMR 模式的终端,修改代码后测试会自动重跑,体验极佳。
🎉 完整工程化闭环总结
Vue 3 + Vite 项目已经具备了企业级的完整骨架:
- 基础架构:Vite + Vue 3 + TS + Pinia + Vue Router
- 工程规范:ESLint + Prettier + Husky + lint-staged
- 性能与体验:Element Plus 按需引入 + 动态权限路由
- 质量保障:Vitest 单测 + 自动化关联测试
你可以直接基于这套底座开始开发业务了