Vitest单元测试教程

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

一、安装

1. 基础安装

bash 复制代码
npm i -D vitest
# 或
yarn add -D vitest
pnpm add -D vitest

2. 常用依赖(按需)

  • 浏览器环境模拟 (前端组件/DOM 测试)

    bash 复制代码
    npm i -D jsdom happy-dom
  • Vue 组件测试

    bash 复制代码
    npm i -D @vue/test-utils
  • React 组件测试

    bash 复制代码
    npm i -D @testing-library/react @testing-library/jest-dom
  • 测试覆盖率

    bash 复制代码
    npm i -D @vitest/coverage-v8
  • UI 界面

    bash 复制代码
    npm 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

八、最佳实践

  1. 测试文件命名xxx.test.ts / xxx.spec.ts
  2. 结构describe 分组 → it/test 用例 → expect 断言
  3. 隔离 :每个用例独立,用 beforeEach/afterEach 重置状态
  4. 测行为不测实现:测输出/交互,不测内部变量
  5. 覆盖率:核心逻辑 ≥ 80%,不盲目追求 100%
相关推荐
DJ斯特拉2 天前
Redis使用lua脚本
junit·单元测试·lua
念越2 天前
蓝桥杯单元测试模拟1期模拟题答案及代码解析
蓝桥杯·单元测试·测试
AIminminHu3 天前
OpenGL渲染与几何内核那点事-项目实践理论补充(三-1-(2):当你的CAD代码变得“又大又乱”:从手动编译到CMake,从随性编码到单元测试))
c++·单元测试·cmake·cad·cad开发
DolphinDB智臾科技4 天前
DolphinDB 单元测试教程
单元测试
春日见4 天前
TEST文件夹:Pytest,集成测试,单元测试
服务器·人工智能·驱动开发·单元测试·计算机外设·集成测试·pytest
川石课堂软件测试4 天前
涨薪技术|Prometheus使用Recoding Rules优化性能
功能测试·测试工具·jmeter·mysql·面试·单元测试·prometheus
龙智DevSecOps解决方案4 天前
TESSY v5.1 新功能详解 :引入 Hyper Coverage 与基于变更的测试,大幅缩短 CI 测试时间
自动化测试·软件测试·ci/cd·单元测试·嵌入式开发·tessy
Joy T4 天前
【Web3】智能合约质量保障工程:从单元测试到 Gas 效能优化
单元测试·log4j·web3·智能合约·hardhat