mcpField
V
vitest

vitest

github.com/jezweb测试与质量1.1k1.7k 次安装3 周前更新
代码仓库

关于 vitest

面向项目环境的 Vitest 配置技能,可生成测试配置、脚本和可运行的示例测试,也涵盖从 Jest 迁移。

主要功能

  1. 01识别项目类型和现有配置
  2. 02生成适配环境的 Vitest 配置
  3. 03创建测试初始化文件和示例
  4. 04添加 TypeScript 设置与脚本
  5. 05指导 Jest 迁移

适用场景

  1. 01为 React 或 Node 项目添加测试
  2. 02配置 Cloudflare Workers 测试
  3. 03为 monorepo 多个包设置 Vitest

安装

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

$npx skills add https://github.com/jezweb/claude-skills --skill vitest

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

详细介绍

Vitest 配置

检测项目类型,生成合适的 Vitest 配置,并构建可用的测试基础设施。这不是参考卡片——此技能会创建文件。

工作流

  1. 检测 — 扫描项目以确定类型和现有配置
  2. 配置 — 生成适合当前环境的 vitest.config.ts
  3. 搭建 — 创建测试设置、工具和示例测试
  4. 接入 — 添加 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 Workerswrangler.toml、@cloudflare/workers-types、Cloudflare Vite 插件配置 Workers 专用设置的 node
React(Vite)@vitejs/plugin-react、react-domjsdom 或 happy-dom
React(SSR/TanStack Start)@tanstack/start、vinxi分开配置:服务端使用 node,客户端使用 jsdom
Node/Hono APIhono、express、没有 react-domnode
库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)时:

  1. 使用上述步骤生成 vitest.config.ts
  2. 更新现有测试文件中的导入:
// 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");
  1. 移除 Jest 软件包:
pnpm remove jest ts-jest @types/jest jest-environment-jsdom babel-jest @jest/globals
  1. 更新 tsconfig — 将 "types": ["jest"] 替换为 "types": ["vitest/globals"]

  2. 运行测试并修复任何遗留问题

主要替换项:

JestVitest
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/globalsvitest
jest.config.jsvitest.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,其中包含测试脚本
  • 至少一个针对真实源代码且能通过的示例测试
  • 已安装的依赖

测试应在首次运行时通过。如果未通过,请在完成前修复。

评论

支持 Markdown