mcpField
V
vitest-v4

vitest-v4

github.com/nodnarbnitram测试与质量1662 次安装27 周前更新
代码仓库

关于 vitest-v4

面向编写、配置和调试测试套件的开发者的 Vitest 4 技能,涵盖 Vite 原生实践、测试执行、模拟、覆盖率、浏览器测试和多项目配置。

主要功能

  1. 01Vitest 4 设置指导
  2. 02模拟与清理模式
  3. 03覆盖率配置
  4. 04Browser Mode 测试
  5. 05多项目配置

适用场景

  1. 01在 CI 中运行非监视模式测试
  2. 02修复测试间模拟对象泄漏
  3. 03为未测试文件配置覆盖率
  4. 04排查 Browser Mode 间谍问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nodnarbnitram/claude-code-extensions --skill vitest-v4

安装后在 Claude Code 中输入/vitest-v4确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
references/
browser-mode-reference.md
coverage-projects-reference.md
mocking-reference.md
pools-execution-reference.md
README.md

详细介绍

Vitest 4 测试技能

使用 Vite 原生模式编写、配置和调试 Vitest 4 测试套件。

开始之前

此技能可避免 7 种以上常见的 Vitest 4 错误,并节省约 50% 的令牌。

指标不使用技能使用技能
设置时间约 90 分钟约 30 分钟
常见错误7 种以上0
令牌用量高(反复试错)低(采用已知模式)
此技能可避免的已知问题
  1. 使用监听模式而非 vitest run,导致 Agent 运行卡住
  2. 使用已移除的 coverage.all 或 coverage.extensions,导致覆盖率配置失效
  3. 浏览器模式下因 ESM 命名空间对象已封闭,导致间谍功能失败
  4. 缺少还原/重置配置,导致 Mock 在测试间泄漏
  5. 使用已弃用的 workspace 术语,导致多项目配置无效
  6. 在 Vitest 测试中混用 Jest 助手,导致 API 错误
  7. 使用合成助手而非 vitest/browser,导致浏览器交互不稳定
  8. 为大型测试套件选择了错误的执行池或隔离模式,导致运行缓慢或不稳定

快速开始

步骤 1:配置 Vitest 4,确保 Agent 安全运行
// 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 移除了 coverage.all 和 coverage.extensions;Agent/CI 环境需要一次性执行并自动清理 Mock。

步骤 2:使用 Vitest API 编写测试,而非 Jest API
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' });
  });
});

原因: vi 是受支持的 Mock API。混用 jest.fn() 或仅适用于 Jest 的模式会导致令人困惑的失败,并造成自动补全效果不佳。

导入规则: 除非项目明确启用了 globals: true,否则请从 vitest 导入 describe、it、expect 和 vi。

步骤 3:使用正确的运行时命令
vitest run
vitest run --coverage
vitest run path/to/example.test.ts

原因: 在开发环境中,不带 run 的 vitest 默认会启动监听模式,不适合 Agent、CI 和非交互式验证。

关键规则

始终遵守
  • 在 Agent 和 CI 工作流中使用 vitest run 或 vitest --no-watch
  • 优先使用 vi.mock(import('./module')) 创建类型安全的模块 Mock
  • 有意配置 restoreMocks、clearMocks 或 mockReset
  • 多项目配置使用 projects;此重命名始于 Vitest 3.2,旧版的 workspace 文件用法已在 Vitest 4 中移除
  • 多个 projects 需要共享设置时,使用共享基础配置;除非选择加入,否则项目不会继承根配置
  • 使用 coverage.include 报告未经测试的源文件
  • 在浏览器模式下使用 vitest/browser 中的 page 和 userEvent
  • 原生模块或运行时兼容性比原始速度更重要时,优先使用 forks
  • 请求 AI 生成测试时,提供 vitest.config.ts 和实现文件
绝不遵守
  • 绝不在 Vitest 代码中使用 jest.fn、jest.spyOn 或仅适用于 Jest 的全局变量
  • 绝不在 Vitest 4 中依赖已移除的 coverage.all 或 coverage.extensions
  • 绝不使用普通监听模式进行 Agent 驱动的验证
  • 绝不在浏览器模式下对原生 ESM 导出使用 vi.spyOn
  • 绝不忘记 vi.mock() 会在文件其余部分执行前提升
  • 绝不让环境变量/全局对象 Stub 在测试间残留
常见错误

错误——使用已移除的覆盖率选项:

export default defineConfig({
  test: {
    coverage: {
      all: true,
    },
  },
});

正确——使用 include glob:

export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      include: ['src/**/*.{ts,tsx}'],
    },
  },
});

原因: Vitest 4 移除了 coverage.all 和 coverage.extensions;coverage.include 是纳入未覆盖文件的受支持方式。

错误——在浏览器模式下对已封闭的导出使用间谍:

import * as math from './math';
import { vi } from 'vitest';

vi.spyOn(math, 'add').mockReturnValue(10);

正确——使用启用间谍的模块 Mock:

import { vi } from 'vitest';

vi.mock(import('./math'), { spy: true });

原因: 原生浏览器 ESM 命名空间对象已封闭,因此在浏览器模式下直接对导出使用间谍会失败。

已知问题的预防

问题根本原因解决方案
测试始终无法退出在非交互式会话中启动了监视模式使用 vitest run
覆盖率报告遗漏未测试的文件未配置 coverage.include添加明确的源文件 glob 模式
Browser Mode spy 报错或不起作用在已封存的 ESM 导出上使用了 vi.spyOn使用 vi.mock(import('./mod'), { spy: true })
模拟在测试之间泄漏缺少清理标志启用 restoreMocks / clearMocks / unstubEnvs
升级后多项目配置失效沿用了已弃用的 workspace 术语或已移除的 workspace 文件模式改用 projects 和 defineProject
Worker 或池配置停止工作沿用了旧的 maxThreads、maxForks 或 poolOptions迁移到 Vitest 4 的 Worker 设置,例如 maxWorkers
项目专属配置意外消失误以为根配置中的设置会被 projects 继承显式使用 extends: true、mergeConfig 或共享基础配置
AI 生成的测试使用了错误的辅助工具将 Jest 模式照搬到了 Vitest改用 vi、Vitest 导入项和 Vitest 匹配器
浏览器测试挂起阻塞式对话框或错误的 user-event 工具模拟对话框并使用 vitest/browser 辅助工具
快速池导致奇怪的原生模块故障为需要进程隔离的测试套件选择了 threads改用 forks 或缩小线程的使用范围

捆绑资源

参考资料

配置参考

vitest.config.ts
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'],
  },
});

关键设置:

  • test.projects:稳定的多项目术语;更名始于 Vitest 3.2,并且项目不会自动继承所有根配置值,因此需要时应将共享设置提取到可重复使用的基础配置中
  • coverage.include:需要在报告中显示未覆盖的源文件时必须设置
  • browser.provider:在 Vitest 4 中,从提供程序包导入提供程序工厂,例如 playwright()
  • restoreMocks / unstubEnvs:防止测试之间相互污染
  • setupFiles:在测试文件运行前执行共享测试初始化,例如 MSW、全局变量或 polyfill

项目结构

my-app/
├── src/
│   ├── feature.ts
│   ├── feature.test.ts
│   └── feature.browser.test.ts
├── vitest.config.ts
├── vite.config.ts
└── package.json

重要性: 清晰分隔 Node 和 Browser Mode 测试,可以大幅简化提供程序设置、测试选择和故障排查。

选择合适的环境: 大多数组件测试和轻量级 DOM 断言优先使用 jsdom。当需要原生浏览器 API、真实的布局或事件行为,或截图断言时,使用 Browser Mode。

选择合适的执行模型: 为了稳定性和原生模块兼容性,尤其是在混合型或基础设施密集型测试套件中,优先使用 forks。只有在确认测试环境适合 Worker 线程执行,且额外速度确有价值时,才使用 threads。

常见模式

类型安全的模块模拟模式
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');
  });
});
Browser Mode 交互模式
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();
});
源码内测试模式
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);
  });
}

依赖项

必需
PackageVersionPurpose
vitest^4测试运行器以及断言/模拟 API
vite^6由 Vite 驱动的共享模块流水线
node>=20Vitest 4 所需的运行时
可选
软件包版本用途
@vitest/coverage-v8^4通过 AST 重映射实现快速、准确的覆盖率统计
@vitest/coverage-istanbul^4Istanbul 覆盖率后端
@vitest/browser-playwright^4Browser Mode 的 Playwright 提供程序
@vitest/browser-webdriverio^4Browser Mode 的 WebdriverIO 提供程序
@vitest/browser-preview^4Browser Mode 的预览提供程序

官方文档

故障排除

Agent 运行一直卡住

症状: 测试进程始终不退出,或 Claude 一直等待文件变更。

解决方案:

vitest run
Browser Mode 测试无法监视导出项

症状: vi.spyOn() 抛出错误、没有效果,或在 Node 模式下正常工作但在浏览器中失败。

解决方案:

vi.mock(import('./module'), { spy: true });
覆盖率统计未包含没有测试的源文件

症状: 报告仅包含已执行测试涉及的文件。

解决方案:

coverage: {
  provider: 'v8',
  include: ['src/**/*.{ts,tsx}'],
}
升级后旧版 worker 或池设置失效

症状: 从 Vitest 4 升级后,旧的 maxThreads、maxForks、singleThread、singleFork 或 poolOptions 设置停止工作。

解决方案:

test: {
  maxWorkers: 4,
}

原因: Vitest 4 简化了 worker 配置,并移除了若干旧版特定于池的选项。

设置检查清单

使用此技能前,请确认:

  • Node.js 为 >=20
  • vite 为 >=6
  • vitest.config.ts 或 vite.config.ts 包含 test 块
  • Agent/CI 命令使用 vitest run
  • 启用覆盖率时已安装覆盖率提供程序软件包
  • 启用 Browser Mode 时已安装浏览器提供程序软件包

评论

支持 Markdown