Vitest 是 Vite 生态的极速单元测试框架,API 兼容 Jest,上手快、配置简单、性能极高。下面从 安装 → 配置 → 编写测试 → 常用断言 → Mock → 组件测试 → 运行与覆盖率 完整流程带你上手。

一、安装
1. 基础安装
bash
npm i -D vitest
# 或
yarn add -D vitest
pnpm add -D vitest
2. 常用依赖(按需)
-
浏览器环境模拟 (前端组件/DOM 测试)
bashnpm i -D jsdom happy-dom -
Vue 组件测试
bashnpm i -D @vue/test-utils -
React 组件测试
bashnpm i -D @testing-library/react @testing-library/jest-dom -
测试覆盖率
bashnpm i -D @vitest/coverage-v8 -
UI 界面
bashnpm i -D @vitest/ui
二、配置(vite.config.ts / vitest.config.ts)
typescript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
// 全局注入 API(describe/it/expect/vi)
globals: true,
// 测试环境:node | jsdom | happy-dom
environment: 'jsdom',
// 测试文件匹配规则
include: ['src/**/*.{test,spec}.{js,ts,jsx,tsx,vue}'],
// 覆盖率配置
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'dist/',
'src/main.ts',
'**/*.d.ts'
]
}
}
})
package.json 脚本
json
{
"scripts": {
"test": "vitest", // 监听模式(开发常用)
"test:run": "vitest run", // 单次运行(CI)
"test:ui": "vitest --ui", // 可视化 UI
"test:coverage": "vitest run --coverage" // 覆盖率
}
}
三、编写第一个测试(工具函数)
1. 待测试代码:src/utils/math.ts
typescript
export function sum(a: number, b: number) {
return a + b
}
export function divide(a: number, b: number) {
if (b === 0) throw new Error('除数不能为 0')
return a / b
}
2. 测试文件:src/utils/math.test.ts
typescript
import { describe, it, expect } from 'vitest'
import { sum, divide } from './math'
// 测试套件(分组)
describe('数学工具', () => {
// 单个测试用例
it('sum 1+2=3', () => {
expect(sum(1, 2)).toBe(3)
})
it('sum 负数相加', () => {
expect(sum(-1, -2)).toBe(-3)
})
it('divide 正常除法', () => {
expect(divide(6, 2)).toBe(3)
})
// 测试异常
it('divide 除以 0 抛出错误', () => {
expect(() => divide(6, 0)).toThrow('除数不能为 0')
})
})
四、常用断言(expect)
typescript
// 基础匹配
expect(2+2).toBe(4) // 严格相等(===)
expect({a:1}).toEqual({a:1}) // 对象深度相等
expect('abc').toContain('b') // 包含
expect(null).toBeNull()
expect(undefined).toBeUndefined()
expect(0).toBeFalsy()
expect(1).toBeTruthy()
// 数字
expect(5).toBeGreaterThan(3)
expect(5).toBeLessThan(10)
// 异常
expect(() => fn()).toThrow('msg')
// 异步
await expect(promise).resolves.toBe('ok')
await expect(promise).rejects.toThrow('err')
五、Mock 模拟(vi)
1. 模拟函数
typescript
import { vi, it, expect } from 'vitest'
const mockFn = vi.fn()
mockFn('a', 1)
expect(mockFn).toHaveBeenCalled()
expect(mockFn).toHaveBeenCalledWith('a', 1)
expect(mockFn).toHaveBeenCalledTimes(1)
// 模拟返回值
mockFn.mockReturnValue(42)
expect(mockFn()).toBe(42)
2. 模拟模块
typescript
import { vi, it, expect } from 'vitest'
import { fetchData } from './api'
// 模拟整个模块
vi.mock('./api', () => ({
fetchData: vi.fn().mockResolvedValue({ data: 'mock' })
}))
it('异步测试', async () => {
const res = await fetchData()
expect(res.data).toBe('mock')
expect(fetchData).toHaveBeenCalledOnce()
})
3. 定时器模拟
typescript
import { vi, it, expect } from 'vitest'
it('定时器测试', () => {
vi.useFakeTimers()
const fn = vi.fn()
setTimeout(fn, 1000)
// 快进时间
vi.advanceTimersByTime(1000)
expect(fn).toHaveBeenCalled()
vi.useRealTimers()
})
六、Vue 组件测试示例
组件:src/components/Counter.vue
vue
<template>
<div>
<p>count: {{ count }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => count.value++
</script>
测试:Counter.test.ts
typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import Counter from './Counter.vue'
describe('Counter 组件', () => {
it('初始 count 为 0', () => {
const wrapper = mount(Counter)
expect(wrapper.text()).toContain('count: 0')
})
it('点击按钮 count +1', async () => {
const wrapper = mount(Counter)
await wrapper.find('button').trigger('click')
expect(wrapper.text()).toContain('count: 1')
})
})
七、运行测试
bash
# 监听模式(修改文件自动重测)
npm test
# 单次运行
npm run test:run
# 可视化 UI(浏览器查看)
npm run test:ui
# 覆盖率报告(生成 coverage/ 目录)
npm run test:coverage
八、最佳实践
- 测试文件命名 :
xxx.test.ts/xxx.spec.ts - 结构 :
describe分组 →it/test用例 →expect断言 - 隔离 :每个用例独立,用
beforeEach/afterEach重置状态 - 测行为不测实现:测输出/交互,不测内部变量
- 覆盖率:核心逻辑 ≥ 80%,不盲目追求 100%