vitest-v4
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVitest 4 Testing Skill
Vitest 4 测试技能
Write, configure, and debug Vitest 4 test suites with Vite-native patterns.
使用Vite原生模式编写、配置和调试Vitest 4测试套件。
Before You Start
开始之前
This skill prevents 7+ common Vitest 4 mistakes and saves ~50% tokens.
| Metric | Without Skill | With Skill |
|---|---|---|
| Setup Time | ~90 min | ~30 min |
| Common Errors | 7+ | 0 |
| Token Usage | High (trial/error) | Low (known patterns) |
本技能可避免7+种常见Vitest 4错误,并节省约50%的token消耗。
| 指标 | 未使用技能 | 使用技能 |
|---|---|---|
| 配置时间 | ~90分钟 | ~30分钟 |
| 常见错误 | 7+ | 0 |
| Token消耗 | 高(反复试错) | 低(使用成熟模式) |
Known Issues This Skill Prevents
本技能可预防的已知问题
- Hanging agent runs from using watch mode instead of
vitest run - Broken coverage configs from using removed or
coverage.allcoverage.extensions - Browser Mode spying failures from sealed ESM namespace objects
- Mock leakage between tests from missing restore/reset config
- Invalid multi-project setup from using deprecated terminology
workspace - Wrong APIs from mixing Jest helpers into Vitest tests
- Flaky browser interactions from using synthetic helpers instead of
vitest/browser - Slow or unstable large suites from choosing the wrong execution pool or isolation mode
- 使用watch模式而非导致Agent运行挂起
vitest run - 使用已移除的或
coverage.all导致覆盖率配置失效coverage.extensions - 浏览器模式下因ESM命名空间对象被密封导致Spy失败
- 缺少恢复/重置配置导致Mock在测试间泄漏
- 使用已弃用的术语导致多项目配置无效
workspace - 在Vitest测试中混用Jest辅助函数导致API错误
- 使用合成辅助函数而非导致浏览器交互不稳定
vitest/browser - 选择错误的执行池或隔离模式导致大型测试套件运行缓慢或不稳定
Quick Start
快速开始
Step 1: Configure Vitest 4 for agent-safe runs
步骤1:配置Vitest 4以支持Agent安全运行
typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
restoreMocks: true,
clearMocks: true,
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
},
},
});Why this matters: Vitest 4 removed and , and agent/CI environments need one-shot execution plus automatic mock cleanup.
coverage.allcoverage.extensionstypescript
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
restoreMocks: true,
clearMocks: true,
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
},
},
});为什么这很重要: Vitest 4移除了和,Agent/CI环境需要一次性执行加上自动Mock清理。
coverage.allcoverage.extensionsStep 2: Write tests with Vitest APIs, not Jest APIs
步骤2:使用Vitest API编写测试,而非Jest API
typescript
import { describe, expect, it, vi } from 'vitest';
import { addUser } from './add-user';
import * as api from './api';
describe('addUser', () => {
it('returns the created user', async () => {
vi.spyOn(api, 'createUser').mockResolvedValue({ id: '1', name: 'Ada' });
await expect(addUser('Ada')).resolves.toEqual({ id: '1', name: 'Ada' });
});
});Why this matters: is the supported mocking API. Mixing or Jest-only patterns causes confusing failures and poor autocomplete.
vijest.fn()Import rule: Import,describe,it, andexpectfromviunless the project explicitly enablesvitest.globals: true
typescript
import { describe, expect, it, vi } from 'vitest';
import { addUser } from './add-user';
import * as api from './api';
describe('addUser', () => {
it('返回创建的用户', async () => {
vi.spyOn(api, 'createUser').mockResolvedValue({ id: '1', name: 'Ada' });
await expect(addUser('Ada')).resolves.toEqual({ id: '1', name: 'Ada' });
});
});为什么这很重要: 是官方支持的Mocking API。混用或Jest专属模式会导致难以排查的失败和糟糕的自动补全体验。
vijest.fn()导入规则: 除非项目明确启用,否则请从globals: true导入vitest、describe、it和expect。vi
Step 3: Use the correct runtime command
步骤3:使用正确的运行命令
bash
vitest run
vitest run --coverage
vitest run path/to/example.test.tsWhy this matters: without starts watch mode by default in development, which is a poor fit for agents, CI, and non-interactive verification.
vitestrunbash
vitest run
vitest run --coverage
vitest run path/to/example.test.ts为什么这很重要: 在开发环境中,不带的默认启动watch模式,这并不适合Agent、CI和非交互式验证场景。
runvitestCritical Rules
关键规则
Always Do
必须遵守
- Use or
vitest runfor agent and CI workflowsvitest --no-watch - Prefer for type-safe module mocks
vi.mock(import('./module')) - Configure ,
restoreMocks, orclearMocksintentionallymockReset - Use for multi-project configs; the rename began in Vitest 3.2 and older workspace-file usage is removed in Vitest 4
projects - Use a shared base config when multiple need common settings; projects do not inherit root config unless you opt in
projects - Use to report on untested source files
coverage.include - Use and
pagefromuserEventin Browser Modevitest/browser - Prefer when native modules or runtime compatibility matter more than raw speed
forks - Share and the implementation file when asking AI to generate tests
vitest.config.ts
- 对于Agent和CI工作流,使用或
vitest runvitest --no-watch - 优先使用实现类型安全的模块Mock
vi.mock(import('./module')) - 有意配置、
restoreMocks或clearMocksmockReset - 多项目配置使用;该重命名始于Vitest 3.2,Vitest 4已移除旧版workspace文件用法
projects - 当多个需要通用设置时,使用共享基础配置;项目不会自动继承根配置,除非你主动开启
projects - 使用报告未测试的源文件
coverage.include - 在浏览器模式中使用提供的
vitest/browser和pageuserEvent - 当原生模块或运行时兼容性比原始速度更重要时,优先使用
forks - 请求AI生成测试时,共享和实现文件
vitest.config.ts
Never Do
绝对禁止
- Never use ,
jest.fn, or Jest-only globals in Vitest codejest.spyOn - Never rely on removed or
coverage.allin Vitest 4coverage.extensions - Never use plain watch mode for agent-driven verification
- Never use on native ESM exports in Browser Mode
vi.spyOn - Never forget that is hoisted before the rest of the file executes
vi.mock() - Never leave env/global stubs un-restored across tests
- 绝不在Vitest代码中使用、
jest.fn或Jest专属全局变量jest.spyOn - 绝不在Vitest 4中依赖已移除的或
coverage.allcoverage.extensions - 绝不为Agent驱动的验证使用普通watch模式
- 绝不在浏览器模式下对原生ESM导出使用
vi.spyOn - 绝不要忘记会在文件其余代码执行前被提升
vi.mock() - 绝不要让环境/全局存根在测试间未恢复
Common Mistakes
常见错误
Wrong - removed coverage option:
typescript
export default defineConfig({
test: {
coverage: {
all: true,
},
},
});Correct - use include globs:
typescript
export default defineConfig({
test: {
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
},
},
});Why: Vitest 4 removed and ; is the supported way to include uncovered files.
coverage.allcoverage.extensionscoverage.includeWrong - Browser Mode spy on sealed export:
typescript
import * as math from './math';
import { vi } from 'vitest';
vi.spyOn(math, 'add').mockReturnValue(10);Correct - use spy-enabled module mock:
typescript
import { vi } from 'vitest';
vi.mock(import('./math'), { spy: true });Why: Native browser ESM namespace objects are sealed, so direct spies on exports fail in Browser Mode.
错误用法 - 已移除的覆盖率选项:
typescript
export default defineConfig({
test: {
coverage: {
all: true,
},
},
});正确用法 - 使用include通配符:
typescript
export default defineConfig({
test: {
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
},
},
});原因: Vitest 4移除了和;是包含未覆盖文件的官方支持方式。
coverage.allcoverage.extensionscoverage.include错误用法 - 浏览器模式下对密封导出使用Spy:
typescript
import * as math from './math';
import { vi } from 'vitest';
vi.spyOn(math, 'add').mockReturnValue(10);正确用法 - 使用支持Spy的模块Mock:
typescript
import { vi } from 'vitest';
vi.mock(import('./math'), { spy: true });原因: 原生浏览器ESM命名空间对象是密封的,因此在浏览器模式下直接对导出进行Spy会失败。
Known Issues Prevention
已知问题预防
| Issue | Root Cause | Solution |
|---|---|---|
| Tests never exit | Watch mode started in a non-interactive session | Use |
| Coverage report misses untested files | | Add explicit source globs |
| Browser Mode spy throws or does nothing | | Use |
| Mocks leak between tests | Cleanup flags missing | Enable |
| Multi-project config breaks after upgrade | Deprecated workspace terminology or removed workspace-file patterns carried over | Switch to |
| Worker or pool config stops working | Old | Migrate to Vitest 4 worker settings such as |
| Project-specific config unexpectedly disappears | Root config assumptions are not inherited into | Use |
| AI-generated tests use wrong helpers | Jest patterns copied into Vitest | Replace with |
| Browser tests hang | Blocking dialogs or wrong user-event utilities | Mock dialogs and use |
| Fast pool causes strange native-module failures | | Switch to |
| 问题 | 根本原因 | 解决方案 |
|---|---|---|
| 测试永不退出 | 在非交互式会话中启动了watch模式 | 使用 |
| 覆盖率报告遗漏未测试文件 | 未配置 | 添加明确的源文件通配符 |
| 浏览器模式Spy抛出错误或无效果 | 对密封ESM导出使用了 | 使用 |
| Mock在测试间泄漏 | 缺少清理标志 | 启用 |
| 升级后多项目配置失效 | 沿用了已弃用的workspace术语或已移除的workspace文件模式 | 切换为 |
| Worker或池配置停止工作 | 沿用了旧版的 | 迁移到Vitest 4的Worker设置,如 |
| 项目特定配置意外消失 | 错误假设根配置会被 | 明确使用 |
| AI生成的测试使用错误的辅助函数 | 将Jest模式复制到Vitest中 | 替换为 |
| 浏览器测试挂起 | 阻塞对话框或使用了错误的用户事件工具 | Mock对话框并使用 |
| Fast pool导致奇怪的原生模块失败 | 为需要进程隔离的套件选择了 | 切换为 |
Bundled Resources
内置资源
References
参考文档
- Mocking rules and hoisting →
references/mocking-reference.md - Browser Mode providers and pitfalls →
references/browser-mode-reference.md - Coverage and multi-project config →
references/coverage-projects-reference.md - Pools, isolation, and persistent cache →
references/pools-execution-reference.md - Reference index →
references/README.md
- Mocking规则与提升机制 →
references/mocking-reference.md - 浏览器模式提供商与陷阱 →
references/browser-mode-reference.md - 覆盖率与多项目配置 →
references/coverage-projects-reference.md - 执行池、隔离与持久缓存 →
references/pools-execution-reference.md - 参考索引 →
references/README.md
Configuration Reference
配置参考
vitest.config.ts
vitest.config.ts
typescript
import { defineConfig, defineProject } from 'vitest/config';
import { playwright } from '@vitest/browser-playwright';
export default defineConfig({
test: {
projects: [
defineProject({
test: {
name: 'unit',
include: ['src/**/*.test.ts'],
environment: 'node',
},
}),
defineProject({
test: {
name: 'browser',
include: ['src/**/*.browser.test.ts'],
browser: {
enabled: true,
provider: playwright(),
instances: [{ browser: 'chromium' }],
},
},
}),
],
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
},
restoreMocks: true,
unstubEnvs: true,
setupFiles: ['./test/setup.ts'],
},
});Key settings:
- : Stable multi-project terminology; the rename started in Vitest 3.2, and projects do not automatically inherit every root config value, so shared settings should be factored into a reused base when needed
test.projects - : Required when uncovered source files must appear in the report
coverage.include - : In Vitest 4, import the provider factory from the provider package, such as
browser.providerplaywright() - /
restoreMocks: Prevent test pollution across filesunstubEnvs - : Run shared test initialization such as MSW, globals, or polyfills before test files
setupFiles
typescript
import { defineConfig, defineProject } from 'vitest/config';
import { playwright } from '@vitest/browser-playwright';
export default defineConfig({
test: {
projects: [
defineProject({
test: {
name: 'unit',
include: ['src/**/*.test.ts'],
environment: 'node',
},
}),
defineProject({
test: {
name: 'browser',
include: ['src/**/*.browser.test.ts'],
browser: {
enabled: true,
provider: playwright(),
instances: [{ browser: 'chromium' }],
},
},
}),
],
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
},
restoreMocks: true,
unstubEnvs: true,
setupFiles: ['./test/setup.ts'],
},
});关键设置:
- : 稳定的多项目术语;重命名始于Vitest 3.2,项目不会自动继承所有根配置值,因此需要时应将共享设置提取到可复用的基础配置中
test.projects - : 当未覆盖的源文件必须出现在报告中时,此配置是必需的
coverage.include - : 在Vitest 4中,从提供商包导入提供商工厂,例如
browser.providerplaywright() - /
restoreMocks: 防止测试文件间的污染unstubEnvs - : 在测试文件运行前执行共享测试初始化,如MSW、全局变量或polyfills
setupFiles
Project Structure
项目结构
my-app/
├── src/
│ ├── feature.ts
│ ├── feature.test.ts
│ └── feature.browser.test.ts
├── vitest.config.ts
├── vite.config.ts
└── package.jsonWhy this matters: Keeping Node and Browser Mode tests clearly separated makes provider setup, test selection, and troubleshooting much simpler.
Choose the right environment: Prefer for most component tests and lightweight DOM assertions. Use Browser Mode when native browser APIs, real layout/event behavior, or screenshot assertions matter.
jsdomChoose the right execution model: Prefer for stability and native-module compatibility, especially in mixed or infrastructure-heavy suites. Reach for only when you know the test environment is safe for worker-thread execution and the extra speed matters.
forksthreadsmy-app/
├── src/
│ ├── feature.ts
│ ├── feature.test.ts
│ └── feature.browser.test.ts
├── vitest.config.ts
├── vite.config.ts
└── package.json为什么这很重要: 清晰分离Node和浏览器模式测试可大幅简化提供商配置、测试选择和故障排查。
选择合适的环境: 大多数组件测试和轻量级DOM断言优先使用。当需要原生浏览器API、真实布局/事件行为或截图断言时,使用浏览器模式。
jsdom选择合适的执行模型: 为了稳定性和原生模块兼容性,优先使用,尤其是在混合或基础设施密集型套件中。仅当确定测试环境适合线程执行且额外速度至关重要时,才使用。
forksthreadsCommon Patterns
常见模式
Type-safe module mock pattern
类型安全模块Mock模式
typescript
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { getUserName } from './get-user-name';
import * as api from './api';
vi.mock(import('./api'), () => ({
fetchUser: vi.fn(),
}));
describe('getUserName', () => {
beforeEach(() => {
vi.mocked(api.fetchUser).mockReset();
});
it('returns the fetched user name', async () => {
vi.mocked(api.fetchUser).mockResolvedValue({ id: '1', name: 'Ada' });
await expect(getUserName('1')).resolves.toBe('Ada');
});
});typescript
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { getUserName } from './get-user-name';
import * as api from './api';
vi.mock(import('./api'), () => ({
fetchUser: vi.fn(),
}));
describe('getUserName', () => {
beforeEach(() => {
vi.mocked(api.fetchUser).mockReset();
});
it('返回获取到的用户名', async () => {
vi.mocked(api.fetchUser).mockResolvedValue({ id: '1', name: 'Ada' });
await expect(getUserName('1')).resolves.toBe('Ada');
});
});Browser Mode interaction pattern
浏览器模式交互模式
typescript
import { expect, test } from 'vitest';
import { page, userEvent } from 'vitest/browser';
import { render } from 'vitest-browser-react';
import { Counter } from './counter';
test('increments after click', async () => {
render(<Counter />);
await userEvent.click(page.getByRole('button', { name: /increment/i }));
await expect.element(page.getByText('1')).toBeInTheDocument();
});typescript
import { expect, test } from 'vitest';
import { page, userEvent } from 'vitest/browser';
import { render } from 'vitest-browser-react';
import { Counter } from './counter';
test('点击后递增', async () => {
render(<Counter />);
await userEvent.click(page.getByRole('button', { name: /increment/i }));
await expect.element(page.getByText('1')).toBeInTheDocument();
});In-source testing pattern
源码内测试模式
typescript
export function sum(a: number, b: number) {
return a + b;
}
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest;
it('adds numbers', () => {
expect(sum(1, 2)).toBe(3);
});
}typescript
export function sum(a: number, b: number) {
return a + b;
}
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest;
it('数字相加', () => {
expect(sum(1, 2)).toBe(3);
});
}Dependencies
依赖项
Required
必需
| Package | Version | Purpose |
|---|---|---|
| ^4 | Test runner and assertion/mocking APIs |
| ^6 | Shared Vite-powered module pipeline |
| >=20 | Required runtime for Vitest 4 |
| 包 | 版本 | 用途 |
|---|---|---|
| ^4 | 测试运行器及断言/Mocking API |
| ^6 | 共享Vite驱动的模块管道 |
| >=20 | Vitest 4所需的运行时 |
Optional
可选
| Package | Version | Purpose |
|---|---|---|
| ^4 | Fast, accurate coverage with AST remapping |
| ^4 | Istanbul coverage backend |
| ^4 | Playwright provider for Browser Mode |
| ^4 | WebdriverIO provider for Browser Mode |
| ^4 | Preview provider for Browser Mode |
| 包 | 版本 | 用途 |
|---|---|---|
| ^4 | 基于AST重映射的快速准确覆盖率工具 |
| ^4 | Istanbul覆盖率后端 |
| ^4 | 浏览器模式的Playwright提供商 |
| ^4 | 浏览器模式的WebdriverIO提供商 |
| ^4 | 浏览器模式的预览提供商 |
Official Documentation
官方文档
Troubleshooting
故障排查
Agent run hangs forever
Agent运行永久挂起
Symptoms: The test process never exits or Claude waits for additional file changes.
Solution:
bash
vitest run症状: 测试进程永不退出,或Claude等待额外的文件更改。
解决方案:
bash
vitest runBrowser Mode test cannot spy on export
浏览器模式测试无法对导出进行Spy
Symptoms: throws, does nothing, or works in Node mode but fails in browser.
vi.spyOn()Solution:
typescript
vi.mock(import('./module'), { spy: true });症状: 抛出错误、无效果,或在Node模式下正常但在浏览器模式下失败。
vi.spyOn()解决方案:
typescript
vi.mock(import('./module'), { spy: true });Coverage misses source files with no tests
覆盖率报告遗漏无测试的源文件
Symptoms: The report only contains files touched by executed tests.
Solution:
typescript
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
}症状: 报告仅包含被执行测试触及的文件。
解决方案:
typescript
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
}Legacy worker or pool settings break after upgrade
升级后旧版Worker或池设置失效
Symptoms: Old , , , , or settings stop working after moving to Vitest 4.
maxThreadsmaxForkssingleThreadsingleForkpoolOptionsSolution:
typescript
test: {
maxWorkers: 4,
}Why: Vitest 4 simplified worker configuration and removed several older pool-specific options.
症状: 迁移到Vitest 4后,旧版的、、、或设置停止工作。
maxThreadsmaxForkssingleThreadsingleForkpoolOptions解决方案:
typescript
test: {
maxWorkers: 4,
}原因: Vitest 4简化了Worker配置,并移除了多个旧版池专属选项。
Setup Checklist
配置检查清单
Before using this skill, verify:
- Node.js is
>=20 - is
vite>=6 - or
vitest.config.tscontains avite.config.tsblocktest - Agent/CI commands use
vitest run - Coverage provider packages are installed if coverage is enabled
- Browser provider packages are installed if Browser Mode is enabled
使用本技能前,请验证:
- Node.js版本
>=20 - 版本
vite>=6 - 或
vitest.config.ts包含vite.config.ts配置块test - Agent/CI命令使用
vitest run - 如果启用了覆盖率,已安装覆盖率提供商包
- 如果启用了浏览器模式,已安装浏览器提供商包