domains / software-engineering / vitest-setup-and-testing / vitest

vitest

Skill card

combined

The copies cover different parts of the Vitest workflow. jezweb/claude-skills is a procedural setup skill that detects project type, installs dependencies, generates vitest.config.ts, and scaffolds test files. antfu/skills and LeoYeAI/openclaw-master-skills are reference catalogs that point to external reference files for API details, mocking, coverage, and workspaces. sickn33/agentic-awesome-skills provides inline patterns for writing tests, mocking, and in-source testing. No single copy contains the full procedure plus the reference material, so crowning one would hide real alternatives.

Repositories

RepositoryPath
jezweb/claude-skillsplugins/dev-tools/skills/vitest/SKILL.md
antfu/skillsskills/vitest/SKILL.md
sickn33/agentic-awesome-skillsplugins/agentic-awesome-skills-claude/skills/vitest-skill/SKILL.md
LeoYeAI/openclaw-master-skillsskills/vitest/SKILL.md

Other copies

RoleRepositoryPath
peerjezweb/claude-skillsplugins/dev-tools/skills/vitest/SKILL.md
peerantfu/skillsskills/vitest/SKILL.md
peersickn33/agentic-awesome-skillsplugins/agentic-awesome-skills-claude/skills/vitest-skill/SKILL.md
peerLeoYeAI/openclaw-master-skillsskills/vitest/SKILL.md
adaptersickn33/agentic-awesome-skillsplugins/agentic-awesome-skills/skills/vitest-skill/SKILL.md
adaptersickn33/agentic-awesome-skillsskills/vitest-skill/SKILL.md

Combined skill

---
name: vitest
description: "Set up Vitest testing in any project and write tests using Vitest's Jest-compatible API. Detects project type, generates vitest.config.ts, test setup, utilities, and sample tests. Covers mocking patterns, coverage config, workspace setup, Jest migration, and in-source testing. Use whenever the user mentions adding tests, setting up Vitest, configuring tests, migrating from Jest, fixing testing infrastructure, or asks 'how do I test this'."
license: MIT
---

# Vitest Setup and Testing

Detect the project type, generate the right Vitest configuration, produce working test infrastructure, and provide reference patterns for writing tests.

## Workflow

1. **Detect** — scan the project to determine type and existing setup
2. **Configure** — generate vitest.config.ts tailored to the environment
3. **Scaffold** — create test setup, utilities, and a sample test
4. **Wire up** — add package.json scripts and TypeScript config
5. **Write tests** — use Vitest API patterns for unit, component, and in-source tests

## Step 1: Detect Project Type

Read these files to determine the project:

```
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
```

Classify as one of:

| Type | Signals | Environment |
|------|---------|-------------|
| **Cloudflare Workers** | wrangler.toml, @cloudflare/workers-types, cloudflare vite plugin | `node` with Workers-specific setup |
| **React (Vite)** | @vitejs/plugin-react, react-dom | `jsdom` or `happy-dom` |
| **React (SSR/TanStack Start)** | @tanstack/start, vinxi | Split: `node` for server, `jsdom` for client |
| **Node/Hono API** | hono, express, no react-dom | `node` |
| **Library** | exports field, no framework deps | `node` |

If a `vite.config.ts` already exists, extend it rather than creating a separate vitest.config.ts — Vitest reads Vite config natively.

## Step 2: Install Dependencies

Generate the install command based on detected type. Use the project's package manager (check for pnpm-lock.yaml, yarn.lock, bun.lockb, or package-lock.json).

```bash
# 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
```

## Step 3: Generate vitest.config.ts

### Cloudflare Workers

```typescript
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";

export default defineWorkersConfig({
  test: {
    globals: true,
    poolOptions: {
      workers: {
        wrangler: { configPath: "./wrangler.toml" },
      },
    },
  },
});
```

If the project uses the Cloudflare Vite plugin (`@cloudflare/vite-plugin`), integrate into the existing vite.config.ts instead:

```typescript
/// <reference types="vitest/config" />
import { defineConfig } from "vite";
import { cloudflare } from "@cloudflare/vite-plugin";

export default defineConfig({
  plugins: [cloudflare()],
  test: {
    globals: true,
  },
});
```

### React (Vite)

```typescript
/// <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,
  },
});
```

If a vite.config.ts already exists, add the `test` block to it rather than creating a new file.

### Node / Hono API

```typescript
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    globals: true,
    environment: "node",
  },
});
```

### With Coverage (add to any config)

```typescript
  test: {
    // ... existing config
    coverage: {
      provider: "v8",
      reporter: ["text", "html", "lcov"],
      exclude: [
        "node_modules/",
        "**/*.config.*",
        "**/*.d.ts",
        "**/test/**",
      ],
    },
  },
```

## Step 4: Generate Test Setup File

Create `src/test/setup.ts` (React projects only):

```typescript
import "@testing-library/jest-dom/vitest";
```

That single import adds all the custom matchers (toBeInTheDocument, toHaveTextContent, etc.) and registers the Vitest `expect.extend` automatically.

## Step 5: Add TypeScript Config

Add to `tsconfig.json` compilerOptions:

```json
{
  "compilerOptions": {
    "types": ["vitest/globals"]
  }
}
```

For projects with multiple tsconfig files (e.g. tsconfig.app.json + tsconfig.node.json), add to the one that covers test files — usually the root tsconfig.json or create a tsconfig.test.json that extends it.

## Step 6: Add Package.json Scripts

```json
{
  "scripts": {
    "test": "vitest",
    "test:run": "vitest run",
    "test:coverage": "vitest run --coverage",
    "test:ui": "vitest --ui"
  }
}
```

Don't overwrite existing scripts — merge with what's there.

## Step 7: Generate Sample Test

Write one test file that demonstrates the right patterns for this specific project. Place it next to real source code, not in a separate `__tests__` directory.

### For a Hono API route (e.g. `src/routes/health.ts`):

```typescript
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" });
  });
});
```

### For a React component (e.g. `src/components/Button.tsx`):

```typescript
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import Button from "./Button";

describe("Button", () => {
  it("renders with label", () => {
    render(<Button label="Click me" />);
    expect(screen.getByText("Click me")).toBeDefined();
  });
});
```

## Test Writing Patterns

### Basic Test

```typescript
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { Calculator } from './calculator';

describe('Calculator', () => {
  let calc: Calculator;
  beforeEach(() => { calc = new Calculator(); });

  it('adds two numbers', () => {
    expect(calc.add(2, 3)).toBe(5);
  });

  it('throws on divide by zero', () => {
    expect(() => calc.divide(10, 0)).toThrow();
  });
});
```

### Mocking (vi instead of jest)

```typescript
import { vi } from 'vitest';

// Mock module
vi.mock('./database', () => ({
  getUser: vi.fn().mockResolvedValue({ name: 'Alice' }),
  saveUser: vi.fn().mockResolvedValue(true),
}));

// Mock function
const mockFn = vi.fn();
mockFn.mockReturnValue(42);
mockFn.mockResolvedValue({ data: 'test' });

// Spy
const spy = vi.spyOn(console, 'log').mockImplementation(() => {});
expect(spy).toHaveBeenCalledWith('message');
spy.mockRestore();

// Timers
vi.useFakeTimers();
vi.advanceTimersByTime(1000);
vi.runAllTimers();
vi.useRealTimers();
```

### In-Source Testing

```typescript
// src/math.ts — tests alongside code!
export function add(a: number, b: number) { return a + b; }
export function multiply(a: number, b: number) { return a * b; }

if (import.meta.vitest) {
  const { it, expect } = import.meta.vitest;
  it('adds', () => { expect(add(2, 3)).toBe(5); });
  it('multiplies', () => { expect(multiply(3, 4)).toBe(12); });
}
```

### Snapshot Testing

```typescript
it('serializes user', () => {
  expect(serializeUser(user)).toMatchSnapshot();
});

it('inline snapshot', () => {
  expect(serializeUser(user)).toMatchInlineSnapshot(`
    { "name": "Alice", "email": "alice@test.com" }
  `);
});
```

### Anti-Patterns

| Bad | Good | Why |
|-----|------|-----|
| `jest.fn()` | `vi.fn()` | Vitest uses `vi` |
| `jest.mock()` | `vi.mock()` | Different namespace |
| No type safety | TypeScript + strict | Vitest is TS-first |

## Quick Reference

| Task | Command |
|------|---------|
| Run once | `npx vitest run` |
| Watch | `npx vitest` (default) |
| UI | `npx vitest --ui` |
| Coverage | `npx vitest --coverage` |
| Specific file | `npx vitest run src/math.test.ts` |
| Filter | `npx vitest run -t "adds"` |

## Reference Topics

For detailed API and feature references, consult the Vitest documentation or the reference files listed in the antfu/skills catalog:

- Configuration: Vitest and Vite config integration, defineConfig usage
- CLI: Command line interface, commands and options
- Test API: test/it function, modifiers like skip, only, concurrent
- Describe API: describe/suite for grouping tests and nested suites
- Expect API: Assertions with toBe, toEqual, matchers and asymmetric matchers
- Hooks: beforeEach, afterEach, beforeAll, afterAll, aroundEach
- Mocking: Mock functions, modules, timers, dates with vi utilities
- Snapshots: Snapshot testing with toMatchSnapshot and inline snapshots
- Coverage: Code coverage with V8 or Istanbul providers
- Test Context: Test fixtures, context.expect, test.extend for custom fixtures
- Concurrency: Concurrent tests, parallel execution, sharding
- Filtering: Filter tests by name, file patterns, tags
- Test Tags: Label tests with tags to filter runs and apply shared options
- Reporters: Built-in reporters, default selection, CI/output config
- Benchmarking: Write benchmarks with the bench fixture (Tinybench)
- Vi Utilities: vi helper: mock, spyOn, fake timers, hoisted, waitFor
- Environments: Test environments: node, jsdom, happy-dom, custom
- Type Testing: Type-level testing with expectTypeOf and assertType
- Projects: Multi-project workspaces, different configs per project

## Limitations

- Use this skill only when the task clearly matches its upstream source and local project context.
- Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.
- Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.