Vitest 高级特性 #
浏览器模式 #
概述 #
Vitest 支持在真实浏览器环境中运行测试,适合测试 DOM 操作、Web API、组件交互等场景。
text
┌─────────────────────────────────────────────────────────────┐
│ 浏览器模式架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Vitest CLI │ -> │ Playwright │ -> │ Browser │ │
│ │ │ │ / Webdriver│ │ Chrome/FF │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ │ │ │ │
│ └──────────────────┴──────────────────┘ │
│ WebSocket 通信 │
│ │
└─────────────────────────────────────────────────────────────┘
安装依赖 #
bash
# 安装浏览器模式提供者
npm install -D @vitest/browser
# 安装浏览器驱动(选择一个)
npm install -D playwright
# 或
npm install -D webdriverio
配置浏览器模式 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
browser: {
enabled: true,
name: 'chrome', // 'chrome' | 'firefox' | 'safari' | 'edge'
provider: 'playwright', // 'playwright' | 'webdriverio'
headless: true,
},
},
})
编写浏览器测试 #
typescript
// button.test.ts
import { expect, test } from 'vitest'
import { page } from '@vitest/browser/context'
test('button click', async () => {
// 渲染组件
document.body.innerHTML = `
<button id="myButton">Click me</button>
`
const button = document.getElementById('myButton')
// 点击按钮
button?.click()
// 验证结果
expect(button?.textContent).toBe('Click me')
})
test('DOM interaction', async () => {
document.body.innerHTML = `
<input id="myInput" type="text" />
<span id="result"></span>
`
const input = document.getElementById('myInput') as HTMLInputElement
const result = document.getElementById('result')
// 模拟输入
input.value = 'Hello'
input.dispatchEvent(new Event('input'))
// 验证
expect(input.value).toBe('Hello')
})
使用 Playwright 工具 #
typescript
import { expect, test } from 'vitest'
import { page } from '@vitest/browser/context'
test('using playwright', async () => {
// 使用 Playwright API
await page.goto('http://localhost:3000')
// 查找元素
const button = page.locator('button')
await button.click()
// 验证
await expect(button).toHaveText('Clicked')
})
工作区 #
概述 #
工作区允许在单个项目中管理多个测试配置,适合 monorepo 或多环境项目。
text
project/
├── packages/
│ ├── core/
│ │ ├── src/
│ │ ├── test/
│ │ └── vitest.config.ts
│ └── ui/
│ ├── src/
│ ├── test/
│ └── vitest.config.ts
├── vitest.workspace.ts
└── package.json
配置工作区 #
typescript
// vitest.workspace.ts
import { defineWorkspace } from 'vitest/config'
export default defineWorkspace([
'packages/core/vitest.config.ts',
'packages/ui/vitest.config.ts',
{
test: {
name: 'integration',
include: ['tests/**/*.test.ts'],
environment: 'node',
},
},
])
包级别配置 #
typescript
// packages/core/vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
name: 'core',
include: ['test/**/*.test.ts'],
environment: 'node',
},
})
// packages/ui/vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
name: 'ui',
include: ['test/**/*.test.ts'],
environment: 'jsdom',
},
})
运行工作区测试 #
bash
# 运行所有工作区测试
vitest
# 运行特定项目
vitest --project=core
vitest --project=ui
In-Source Testing #
概述 #
In-Source Testing 允许在源代码文件中直接编写测试,无需创建单独的测试文件。
基本用法 #
typescript
// utils.ts
export function add(a: number, b: number): number {
return a + b
}
// 在源文件中编写测试
if (import.meta.vitest) {
const { test, expect } = import.meta.vitest
test('add', () => {
expect(add(1, 2)).toBe(3)
})
}
配置 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
includeSource: ['src/**/*.{ts,js}'],
},
})
优势 #
- 测试与实现代码放在一起
- 更容易保持测试和实现同步
- 适合小型工具函数
自定义报告器 #
创建自定义报告器 #
typescript
// custom-reporter.ts
import type { Reporter, TestCase, TestSuite } from 'vitest'
export default class CustomReporter implements Reporter {
onInit() {
console.log('Test run starting...')
}
onFinished(files: TestSuite[]) {
console.log('Test run finished!')
console.log(`Total files: ${files.length}`)
}
onTaskUpdate(task: TestCase) {
if (task.result?.state === 'pass') {
console.log(`✓ ${task.name}`)
} else if (task.result?.state === 'fail') {
console.log(`✗ ${task.name}`)
}
}
}
使用自定义报告器 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest'
import CustomReporter from './custom-reporter'
export default defineConfig({
test: {
reporters: [
'default',
new CustomReporter(),
],
},
})
内置报告器 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest'
export default defineConfig({
test: {
reporters: [
'default', // 默认报告器
'verbose', // 详细报告
'dot', // 点状报告
'json', // JSON 报告
'html', // HTML 报告
'junit', // JUnit XML 报告
],
},
})
调试测试 #
使用 VS Code 调试 #
json
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Current Test File",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/vitest",
"runtimeArgs": [
"run",
"${file}"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
使用 console.log 调试 #
typescript
import { test, expect } from 'vitest'
test('debug with console', () => {
const data = { a: 1, b: 2 }
console.log('data:', data)
console.table(data)
expect(data.a).toBe(1)
})
使用 debug 模块 #
typescript
// 安装 debug
// npm install -D debug
import { test, expect } from 'vitest'
import debug from 'debug'
const log = debug('myapp:test')
test('debug test', () => {
log('Starting test...')
const result = complexOperation()
log('Result: %o', result)
expect(result).toBeDefined()
})
使用 Node.js 调试器 #
bash
# 启动调试模式
node --inspect-brk ./node_modules/.bin/vitest run
# 然后在 Chrome DevTools 中连接
# chrome://inspect
测试过滤 #
按名称过滤 #
bash
# 运行匹配名称的测试
vitest run -t "user"
vitest run -t "should create"
按文件过滤 #
bash
# 运行特定文件
vitest run test/user.test.ts
# 运行匹配模式的文件
vitest run "test/**/*.test.ts"
按项目过滤 #
bash
# 运行特定项目
vitest run --project=core
按变更过滤 #
bash
# 只运行变更文件相关的测试
vitest run --changed
# 指定基准分支
vitest run --changed main
测试隔离 #
线程模式 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest'
export default defineConfig({
test: {
// 多线程运行(默认)
threads: true,
// 设置线程数
maxThreads: 4,
minThreads: 1,
// 或单线程运行
// threads: false,
},
})
隔离模式 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest'
export default defineConfig({
test: {
// 每个测试文件在独立环境中运行
isolate: true,
// 每个测试文件单独进程
// isolate: false 会共享环境
},
})
测试钩子 #
全局钩子 #
typescript
// test/setup.ts
import { beforeAll, afterAll, beforeEach, afterEach } from 'vitest'
beforeAll(() => {
console.log('All tests starting')
})
afterAll(() => {
console.log('All tests finished')
})
beforeEach(() => {
console.log('Each test starting')
})
afterEach(() => {
console.log('Each test finished')
})
配置全局钩子 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest'
export default defineConfig({
test: {
setupFiles: ['./test/setup.ts'],
globalSetup: ['./test/globalSetup.ts'],
},
})
globalSetup 文件 #
typescript
// test/globalSetup.ts
export default function setup() {
console.log('Global setup')
// 返回 teardown 函数
return function teardown() {
console.log('Global teardown')
}
}
快照管理 #
快照配置 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest'
export default defineConfig({
test: {
snapshotFormat: {
escapeString: true,
printBasicPrototype: true,
indent: 2,
},
// 快照更新模式
update: false, // 'all' | 'new' | 'none'
},
})
快照操作 #
bash
# 更新所有快照
vitest -u
# 只更新失败的快照
vitest -u --run
# 更新新快照
vitest --update=new
测试环境变量 #
设置环境变量 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest'
export default defineConfig({
test: {
env: {
NODE_ENV: 'test',
API_URL: 'http://test-api.example.com',
},
},
})
在测试中使用 #
typescript
import { test, expect } from 'vitest'
test('environment variables', () => {
expect(process.env.NODE_ENV).toBe('test')
expect(process.env.API_URL).toBe('http://test-api.example.com')
})
测试依赖 #
指定依赖 #
typescript
import { test, expect } from 'vitest'
test('with dependencies', () => {
// ...
}, {
// 指定依赖文件
depends: ['./setup.ts'],
})
测试并发控制 #
并发配置 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest'
export default defineConfig({
test: {
// 全局并发
sequence: {
concurrent: true,
},
// 或在测试中控制
// test.concurrent()
},
})
串行测试 #
typescript
import { test, describe } from 'vitest'
describe.serial('serial tests', () => {
test('test 1', async () => {
// 等待完成
})
test('test 2', async () => {
// test 1 完成后执行
})
})
最佳实践 #
1. 合理组织测试文件 #
text
project/
├── src/
│ ├── utils/
│ │ ├── sum.ts
│ │ └── sum.test.ts
│ └── components/
│ ├── Button.tsx
│ └── Button.test.tsx
├── test/
│ ├── setup.ts
│ ├── integration/
│ └── e2e/
└── vitest.config.ts
2. 使用 TypeScript 类型 #
typescript
// test/utils.ts
import { expect } from 'vitest'
export function expectType<T>(value: T): void {
// 类型检查
}
// 使用
test('type safety', () => {
const result = calculate()
expectType<number>(result)
})
3. 配置合理的超时 #
typescript
// vitest.config.ts
import { defineConfig } from 'vitest'
export default defineConfig({
test: {
testTimeout: 10000,
hookTimeout: 10000,
},
})
4. 使用测试工具函数 #
typescript
// test/helpers.ts
import { vi } from 'vitest'
export function createMockResponse<T>(data: T): Response {
return {
ok: true,
json: async () => data,
} as Response
}
export function waitFor(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
5. 清理测试副作用 #
typescript
import { test, beforeEach, afterEach, vi } from 'vitest'
beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.useRealTimers()
vi.clearAllMocks()
})
test('clean test', () => {
// 测试代码
})
高级特性速查表 #
| 特性 | 说明 |
|---|---|
| 浏览器模式 | 在真实浏览器中运行测试 |
| 工作区 | 管理多个测试配置 |
| In-Source Testing | 在源文件中编写测试 |
| 自定义报告器 | 创建自定义测试报告 |
| 调试 | VS Code / Node.js 调试 |
| 测试过滤 | 按名称/文件/项目过滤 |
| 测试隔离 | 线程/进程隔离 |
| 全局钩子 | 全局 setup/teardown |
总结 #
恭喜你完成了 Vitest 文档的学习!现在你已经掌握了:
- 基础概念:了解 Vitest 的特点和优势
- 安装配置:学会配置 Vitest 测试环境
- 编写测试:掌握测试结构和生命周期
- 断言方法:使用各种断言验证结果
- Mock 功能:模拟函数和模块
- 快照测试:测试 UI 组件和数据结构
- 异步测试:处理异步代码测试
- 代码覆盖率:测量测试覆盖程度
- 性能测试:进行基准测试对比
- 高级特性:浏览器模式、工作区等
继续实践,将测试融入日常开发流程,提高代码质量和开发效率!
最后更新:2026-03-28