关于 vitest
面向项目环境的 Vitest 配置技能,可生成测试配置、脚本和可运行的示例测试,也涵盖从 Jest 迁移。
主要功能
- 01识别项目类型和现有配置
- 02生成适配环境的 Vitest 配置
- 03创建测试初始化文件和示例
- 04添加 TypeScript 设置与脚本
- 05指导 Jest 迁移
适用场景
- 01为 React 或 Node 项目添加测试
- 02配置 Cloudflare Workers 测试
- 03为 monorepo 多个包设置 Vitest
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jezweb/ claude-skills --skill vitest安装后在 Claude Code 中输入/vitest确认已加载。
详细介绍
Vitest 配置
检测项目类型,生成合适的 Vitest 配置,并构建可用的测试基础设施。这不是参考卡片——此技能会创建文件。
工作流
- 检测 — 扫描项目以确定类型和现有配置
- 配置 — 生成适合当前环境的 vitest.config.ts
- 搭建 — 创建测试设置、工具和示例测试
- 接入 — 添加 package.json 脚本和 TypeScript 配置
步骤 1:检测项目类型
读取以下文件以确定项目类型:
package.json → dependencies, scripts, type field
tsconfig.json → paths, compiler options
wrangler.toml → Cloudflare Workers project
vite.config.ts → existing Vite setup (extend, don't replace)
vitest.config.ts → already configured? just fill gaps
jest.config.* → migration candidate
src/ → source structure
分类为以下类型之一:
| 类型 | 特征 | 环境 |
|---|---|---|
| Cloudflare Workers | wrangler.toml、@cloudflare/workers-types、Cloudflare Vite 插件 | 配置 Workers 专用设置的 node |
| React(Vite) | @vitejs/plugin-react、react-dom | jsdom 或 happy-dom |
| React(SSR/TanStack Start) | @tanstack/start、vinxi | 分开配置:服务端使用 node,客户端使用 jsdom |
| Node/Hono API | hono、express、没有 react-dom | node |
| 库 | exports 字段、没有框架依赖 | node |
如果已经存在 vite.config.ts,请扩展它,而不要单独创建 vitest.config.ts——Vitest 会原生读取 Vite 配置。
步骤 2:安装依赖
根据检测到的类型生成安装命令:
# Base (always)
pnpm add -D vitest
# React projects — add jsdom and Testing Library
pnpm add -D @vitest/coverage-v8 jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event
# Workers projects — add Cloudflare test utilities
pnpm add -D @vitest/coverage-v8 @cloudflare/vitest-pool-workers
# Node/Hono projects
pnpm add -D @vitest/coverage-v8
# If migrating from Jest, also remove:
pnpm remove jest ts-jest @types/jest jest-environment-jsdom babel-jest
使用项目的包管理器(检查是否存在 pnpm-lock.yaml、yarn.lock、bun.lockb 或 package-lock.json)。
步骤 3:生成 vitest.config.ts
Cloudflare Workers
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
export default defineWorkersConfig({
test: {
globals: true,
poolOptions: {
workers: {
wrangler: { configPath: "./wrangler.toml" },
},
},
},
});
如果项目使用 Cloudflare Vite 插件(@cloudflare/vite-plugin),请改为将其集成到现有 vite.config.ts 中:
/// <reference types="vitest/config" />
import { defineConfig } from "vite";
import { cloudflare } from "@cloudflare/vite-plugin";
export default defineConfig({
plugins: [cloudflare()],
test: {
globals: true,
},
});
React(Vite)
/// <reference types="vitest/config" />
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: "jsdom",
setupFiles: ["./src/test/setup.ts"],
css: true,
},
});
如果已经存在 vite.config.ts,请将 test 块添加到该文件中,而不要创建新文件。
Node / Hono API
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
globals: true,
environment: "node",
},
});
使用覆盖率(添加到任意配置)
test: {
// ... existing config
coverage: {
provider: "v8",
reporter: ["text", "html", "lcov"],
exclude: [
"node_modules/",
"**/*.config.*",
"**/*.d.ts",
"**/test/**",
],
},
},
步骤 4:生成测试设置文件
创建 src/test/setup.ts(仅限 React 项目):
import "@testing-library/jest-dom/vitest";
只需导入这一项,即可添加所有自定义匹配器(toBeInTheDocument、toHaveTextContent 等),并自动注册 Vitest expect.extend。
步骤 5:添加 TypeScript 配置
将以下内容添加到 tsconfig.json 的 compilerOptions 中:
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}
对于包含多个 tsconfig 文件的项目(例如 tsconfig.app.json + tsconfig.node.json),请添加到涵盖测试文件的配置文件中——通常是根目录下的 tsconfig.json;也可以创建一个继承它的 tsconfig.test.json。
步骤 6:添加 package.json 脚本
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
"test:ui": "vitest --ui"
}
}
不要覆盖现有脚本——请合并添加。
步骤 7:生成示例测试
编写一个测试文件,演示此项目适用的模式。将文件放在真实源代码旁边,而不是单独放在 __tests__ 目录中。
Hono API 路由示例(如 src/routes/health.ts):
import { describe, it, expect } from "vitest";
import { app } from "../index";
describe("GET /health", () => {
it("returns 200 with status ok", async () => {
const res = await app.request("/health");
expect(res.status).toBe(200);
const body = await res.json();
expect(body).toEqual({ status: "ok" });
});
});
React 组件示例(如 src/components/Button.tsx):
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Button } from "./Button";
describe("Button", () => {
it("renders with label", () => {
render(<Button>Click me</Button>);
expect(screen.getByRole("button", { name: /click me/i })).toBeInTheDocument();
});
it("calls onClick when clicked", async () => {
const user = userEvent.setup();
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click me</Button>);
await user.click(screen.getByRole("button"));
expect(handleClick).toHaveBeenCalledOnce();
});
});
工具函数示例(如 src/utils/format.ts):
import { describe, it, expect } from "vitest";
import { formatCurrency } from "./format";
describe("formatCurrency", () => {
it("formats whole numbers", () => {
expect(formatCurrency(1000)).toBe("$1,000.00");
});
it("formats decimals", () => {
expect(formatCurrency(49.9)).toBe("$49.90");
});
it("handles zero", () => {
expect(formatCurrency(0)).toBe("$0.00");
});
});
从项目中选择一个真实文件进行测试。不要虚构模块——完成设置后,示例测试应能立即运行。
步骤 8:验证
运行测试,确认一切正常:
pnpm test:run
如果失败,请诊断并修复。常见问题:
| 错误 | 修复方法 |
|---|---|
Cannot find module 'vitest' | 确认安装已完成,并检查 node_modules/.vitest 是否存在 |
ReferenceError: describe is not defined | 在配置中添加 globals: true,或在 tsconfig 中添加 types: ["vitest/globals"] |
document is not defined | 环境不正确——为 React 测试设置 environment: "jsdom" |
Cannot use import.meta | 确保 vitest.config 使用 .ts 扩展名,且项目设置了 "type": "module",或者由 Vite 处理转换 |
| Workers 绑定未定义 | 使用 @cloudflare/vitest-pool-workers,而不是普通的 vitest,并检查 wrangler.toml 路径 |
模拟参考
这些模式用于完成设置后编写测试。如果用户需要模拟示例,请将它们加入示例测试,或加入 src/test/examples.test.ts。
模块模拟(vi.mock)
import { vi, describe, it, expect } from "vitest";
import { getUser } from "./api";
vi.mock("./api", () => ({
getUser: vi.fn(),
}));
it("mocks a module function", async () => {
vi.mocked(getUser).mockResolvedValue({ id: 1, name: "Test" });
const user = await getUser(1);
expect(user.name).toBe("Test");
expect(getUser).toHaveBeenCalledWith(1);
});
监视方法(vi.spyOn)
it("spies on console.warn", () => {
const spy = vi.spyOn(console, "warn").mockImplementation(() => {});
doSomethingThatWarns();
expect(spy).toHaveBeenCalledOnce();
spy.mockRestore();
});
模拟计时器
import { vi, beforeEach, afterEach, it, expect } from "vitest";
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-01-15T10:00:00Z"));
});
afterEach(() => {
vi.useRealTimers();
});
it("uses controlled time", () => {
expect(new Date().toISOString()).toBe("2026-01-15T10:00:00.000Z");
});
全局存根
it("stubs fetch", async () => {
const mockFetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ data: "test" }),
});
vi.stubGlobal("fetch", mockFetch);
const res = await fetch("/api/data");
expect(mockFetch).toHaveBeenCalledWith("/api/data");
vi.unstubAllGlobals();
});
快照测试
it("matches snapshot", () => {
const result = generateConfig({ debug: true });
expect(result).toMatchSnapshot();
});
it("matches inline snapshot", () => {
expect({ status: "ok", count: 3 }).toMatchInlineSnapshot(`
{
"count": 3,
"status": "ok",
}
`);
});
参数化测试
describe.each([
{ input: "hello", expected: "HELLO" },
{ input: "world", expected: "WORLD" },
{ input: "", expected: "" },
])("toUpperCase($input)", ({ input, expected }) => {
it(`returns ${expected}`, () => {
expect(input.toUpperCase()).toBe(expected);
});
});
Jest 迁移
当检测到项目使用 Jest(依赖中存在 jest.config.*、@types/jest、ts-jest)时:
- 使用上述步骤生成 vitest.config.ts
- 更新现有测试文件中的导入:
// Before
import { jest } from "@jest/globals";
jest.mock("./api");
jest.fn();
jest.spyOn(obj, "method");
// After
import { vi } from "vitest";
vi.mock("./api");
vi.fn();
vi.spyOn(obj, "method");
- 移除 Jest 软件包:
pnpm remove jest ts-jest @types/jest jest-environment-jsdom babel-jest @jest/globals
-
更新 tsconfig — 将
"types": ["jest"]替换为"types": ["vitest/globals"] -
运行测试并修复任何遗留问题
主要替换项:
| Jest | Vitest |
|---|---|
jest.fn() | vi.fn() |
jest.mock() | vi.mock() |
jest.spyOn() | vi.spyOn() |
jest.useFakeTimers() | vi.useFakeTimers() |
jest.clearAllMocks() | vi.clearAllMocks() |
jest.requireActual() | vi.importActual() |
@jest/globals | vitest |
jest.config.js | vitest.config.ts |
工作区设置(Monorepo)
对于包含多个软件包的 monorepo 项目:
// vitest.workspace.ts
import { defineWorkspace } from "vitest/config";
export default defineWorkspace([
"packages/*/vitest.config.ts",
]);
每个软件包都有自己的配置文件。工作区文件只需指向这些配置文件。
此技能会生成什么
运行后,项目应包含:
vitest.config.ts(或在现有 vite.config.ts 中添加测试配置块)src/test/setup.ts(React 项目)- 更新后的
tsconfig.json,其中包含 vitest/globals 类型 - 更新后的
package.json,其中包含测试脚本 - 至少一个针对真实源代码且能通过的示例测试
- 已安装的依赖
测试应在首次运行时通过。如果未通过,请在完成前修复。