name: vitest-configuration user-invocable: false description: 当需要 Vitest 配置、Vite 集成、工作区设置和现代测试的测试环境配置时使用。 allowed-tools: [Read, Write, Edit, Bash, Glob, Grep]
Vitest 配置
掌握 Vitest 配置、Vite 集成、工作区设置和现代测试的测试环境配置。此技能涵盖了 Vitest(由 Vite 驱动的快速单元测试框架)的全面配置策略。
安装和设置
基本安装
npm install -D vitest
# or
yarn add -D vitest
# or
pnpm add -D vitest
附加包
# Vitest 的 UI
npm install -D @vitest/ui
# 浏览器模式
npm install -D @vitest/browser playwright
# 覆盖率
npm install -D @vitest/coverage-v8
# or
npm install -D @vitest/coverage-istanbul
配置文件
vitest.config.ts(推荐)
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
// 测试环境
environment: 'node', // 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime'
// 全局测试文件
globals: true,
setupFiles: ['./vitest.setup.ts'],
// 包含/排除模式
include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
exclude: ['node_modules', 'dist', '.idea', '.git', '.cache'],
// 覆盖率配置
coverage: {
provider: 'v8', // 'v8' | 'istanbul'
reporter: ['text', 'json', 'html'],
include: ['src/**/*.{js,ts,jsx,tsx}'],
exclude: [
'node_modules/',
'src/**/*.test.{js,ts,jsx,tsx}',
'src/**/*.spec.{js,ts,jsx,tsx}',
'src/**/__tests__/**'
],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80
}
},
// 性能
pool: 'threads', // 'threads' | 'forks' | 'vmThreads'
poolOptions: {
threads: {
singleThread: false,
minThreads: 1,
maxThreads: 4
}
},
// 超时设置
testTimeout: 10000,
hookTimeout: 10000,
// 监听选项
watch: false,
watchExclude: ['**/node_modules/**', '**/dist/**'],
// 报告器
reporters: ['default'],
// 模拟选项
mockReset: true,
restoreMocks: true,
clearMocks: true
}
});
扩展 Vite 配置
import { defineConfig, mergeConfig } from 'vitest/config';
import viteConfig from './vite.config';
export default mergeConfig(
viteConfig,
defineConfig({
test: {
// Vitest 特定配置
}
})
);
工作区配置
// vitest.workspace.ts
import { defineWorkspace } from 'vitest/config';
export default defineWorkspace([
// 多个项目
{
extends: './vitest.config.ts',
test: {
name: 'unit',
include: ['src/**/*.test.ts'],
environment: 'node'
}
},
{
extends: './vitest.config.ts',
test: {
name: 'browser',
include: ['src/**/*.browser.test.ts'],
environment: 'jsdom'
}
},
{
extends: './vitest.config.ts',
test: {
name: 'integration',
include: ['tests/integration/**/*.test.ts'],
environment: 'node'
}
}
]);
环境配置
Node 环境
// vitest.config.ts
export default defineConfig({
test: {
environment: 'node',
environmentOptions: {
// Node 特定选项
}
}
});
JSDOM 环境
// vitest.config.ts
export default defineConfig({
test: {
environment: 'jsdom',
environmentOptions: {
jsdom: {
resources: 'usable',
url: 'http://localhost:3000'
}
}
}
});
Happy DOM 环境
// vitest.config.ts
export default defineConfig({
test: {
environment: 'happy-dom',
environmentOptions: {
happyDOM: {
width: 1024,
height: 768
}
}
}
});
自定义环境
// custom-environment.ts
import type { Environment } from 'vitest';
export default <Environment>{
name: 'custom',
transformMode: 'ssr',
setup() {
// 设置自定义环境
return {
teardown() {
// 清理
}
};
}
};
// vitest.config.ts
export default defineConfig({
test: {
environment: './custom-environment.ts'
}
});
设置文件
vitest.setup.ts
import { expect, afterEach, vi } from 'vitest';
import { cleanup } from '@testing-library/react';
import matchers from '@testing-library/jest-dom/matchers';
// 扩展 Vitest 匹配器
expect.extend(matchers);
// 每次测试后清理
afterEach(() => {
cleanup();
});
// 模拟全局对象
global.fetch = vi.fn();
// 设置全局测试工具
global.testUtils = {
// 自定义测试工具
};
// 配置测试环境
beforeAll(() => {
// 全局设置
});
afterAll(() => {
// 全局清理
});
React 测试设置
import { expect, afterEach } from 'vitest';
import { cleanup } from '@testing-library/react';
import * as matchers from '@testing-library/jest-dom/matchers';
expect.extend(matchers);
afterEach(() => {
cleanup();
});
// 模拟 window.matchMedia
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn()
}))
});
覆盖率配置
V8 提供者
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html', 'lcov'],
reportsDirectory: './coverage',
include: ['src/**/*.ts'],
exclude: [
'**/*.test.ts',
'**/*.spec.ts',
'**/types/**',
'**/*.d.ts'
],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
perFile: true
},
all: true,
clean: true,
cleanOnRerun: true
}
}
});
Istanbul 提供者
export default defineConfig({
test: {
coverage: {
provider: 'istanbul',
reporter: ['text', 'json', 'html'],
watermarks: {
lines: [80, 95],
functions: [80, 95],
branches: [80, 95],
statements: [80, 95]
}
}
}
});
模块解析
路径别名
import { defineConfig } from 'vitest/config';
import path from 'path';
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@utils': path.resolve(__dirname, './src/utils'),
'@hooks': path.resolve(__dirname, './src/hooks'),
'@services': path.resolve(__dirname, './src/services')
}
},
test: {
// 测试配置
}
});
外部依赖
export default defineConfig({
test: {
// 不要外部化这些包
deps: {
inline: ['package-to-inline']
},
// 外部化这些包
server: {
deps: {
external: ['package-to-external']
}
}
}
});
Package.json 脚本
{
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
"test:watch": "vitest watch",
"test:ci": "vitest run --coverage --reporter=json --reporter=default"
}
}
高级配置
浏览器模式
export default defineConfig({
test: {
browser: {
enabled: true,
name: 'chrome', // 'chrome' | 'firefox' | 'safari'
provider: 'playwright', // 'playwright' | 'webdriverio'
headless: true,
screenshotFailures: true
}
}
});
性能优化
export default defineConfig({
test: {
// 使用线程进行并行执行
pool: 'threads',
poolOptions: {
threads: {
singleThread: false,
minThreads: 1,
maxThreads: 4,
useAtomics: true
}
},
// 隔离测试
isolate: true,
// 序列化测试
sequence: {
shuffle: false,
concurrent: false
},
// 文件并行性
fileParallelism: true,
// 最大并发数
maxConcurrency: 5,
// 失败时中止
bail: 1
}
});
类型检查
export default defineConfig({
test: {
typecheck: {
enabled: true,
checker: 'tsc', // 'tsc' | 'vue-tsc'
tsconfig: './tsconfig.json',
include: ['**/*.{test,spec}-d.ts']
}
}
});
最佳实践
- 使用 TypeScript 配置 - 在配置文件中利用类型安全
- 配置适当的环境 - 为测试选择正确的环境(node vs jsdom)
- 设置覆盖率阈值 - 定义现实的覆盖率目标
- 使用工作区处理 monorepo - 利用工作区配置处理多个项目
- 配置路径别名 - 匹配应用程序的导入路径
- 优化线程使用 - 平衡并行性与系统资源
- 谨慎使用全局变量 - 优先使用显式导入以更好地进行树摇
- 配置适当的超时时间 - 为异步操作设置现实的超时
- 启用覆盖率报告 - 一致地跟踪测试覆盖率
- 有效使用设置文件 - 集中化通用设置逻辑
常见陷阱
- 错误的环境选择 - 使用错误环境导致未定义错误
- 缺少路径别名 - 忘记从 vite.config 配置别名
- 过于激进的覆盖率阈值 - 不切实际的目标阻碍测试
- 未配置全局变量 - 导致每个测试文件都需要冗长的导入
- 错误的线程配置 - 过多线程压倒系统
- 缺少设置文件 - 每个测试中重复的样板代码
- 错误的覆盖率提供者 - V8 与 Istanbul 能力不同
- 未清理模拟 - 共享模拟状态导致不稳定的测试
- 忽略监听排除 - 不必要的文件监听减慢开发速度
- 错误配置的模块解析 - 测试文件中的导入错误
何时使用此技能
- 在新 Vite 项目中设置 Vitest
- 从 Jest 迁移到 Vitest
- 为 TypeScript 项目配置 Vitest
- 使用工作区设置 monorepo 中的测试
- 为大型代码库优化测试性能
- 配置使用 Playwright 的浏览器测试
- 为 CI/CD 设置覆盖率报告
- 调试模块解析问题
- 实现自定义测试环境
- 配置测试的类型检查