vitest

📁 paulrberg/dot-agents 📅 Today
0
总安装量
1
周安装量
安装命令
npx skills add https://github.com/paulrberg/dot-agents --skill vitest

Agent 安装分布

amp 1
cline 1
opencode 1
cursor 1
continue 1
kimi-cli 1

Skill 文档

Your Role

You are an expert in writing tests with Vitest v4 for TypeScript React/Next.js projects. You help users write high-quality tests, debug failures, and maintain test suites efficiently.

Typical setup:

  • Vitest v4 with jsdom environment
  • Globals enabled (describe, test, expect, vi)
  • Path aliases configured per project

Skill delegation:

If the task is related to Zustand store testing, activate the zustand skill

Quick Start

Running Tests

# Run all unit tests
nlx vitest run

# Run tests matching pattern
nlx vitest run tokens

# Run specific test file
nlx vitest run src/utils/format.test.ts

# Run tests with matching name
nlx vitest run -t "adds token"

# Watch mode
nlx vitest

Writing Your First Test

File naming: *.test.ts or *.test.tsx

Location: Colocate with source files

import { describe, test, expect } from "vitest";
import { myFunction } from "./my-function";

describe("myFunction", () => {
  test("returns expected value", () => {
    expect(myFunction(5)).toBe(10);
  });
});

Project-Specific Patterns

Test Organization

Use visual separators and descriptive blocks:

describe("TokenStore", () => {
  /* ----------------------------------------------------------------
   * Setup
   * ------------------------------------------------------------- */

  const validToken = { address: "0x123", symbol: "TEST" };

  afterEach(() => {
    // Reset state between tests
    useTokensStore.getState().clearAll();
  });

  /* ----------------------------------------------------------------
   * Adding tokens
   * ------------------------------------------------------------- */

  describe("addToken", () => {
    test("adds valid token and returns true", () => {
      const success = useTokensStore.getState().addToken(validToken);
      expect(success).toBe(true);
    });
  });
});

Cleanup Pattern

Always reset state in afterEach():

import { afterEach } from "vitest";

afterEach(() => {
  // Reset mocks
  vi.clearAllMocks();

  // Reset environment
  process.env.NODE_ENV = originalEnv;

  // Reset stores (if not using zustand skill)
  // For Zustand stores, use the `zustand` skill
});

Factory Mock Pattern

Prefer factory functions for complex mocks:

// __mocks__/localStorage.ts
import { vi } from "vitest";

export function createLocalStorageMock() {
  const store = new Map<string, string>();

  return {
    getItem: vi.fn((key: string) => store.get(key) ?? null),
    setItem: vi.fn((key: string, value: string) => {
      store.set(key, value);
    }),
    removeItem: vi.fn((key: string) => {
      store.delete(key);
    }),
    clear: vi.fn(() => {
      store.clear();
    })
  };
}

// Usage in tests
import { createLocalStorageMock } from "./__mocks__/localStorage";

const mockStorage = createLocalStorageMock();
global.localStorage = mockStorage as Storage;

Shared Setup File

Global mocks and configuration live in a setup file (e.g., tests/setup.ts):

import { vi } from "vitest";

// Mock logger for all tests
vi.mock("@/utils/logger", () => ({
  createLogger: vi.fn(() => ({
    debug: vi.fn(),
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn()
  }))
}));

Common Testing Scenarios

Testing Utilities

import { describe, test, expect, afterEach } from "vitest";
import { getEnvironment } from "./environment";

describe("getEnvironment", () => {
  const originalEnv = process.env.NODE_ENV;

  afterEach(() => {
    process.env.NODE_ENV = originalEnv;
  });

  test("returns production when NODE_ENV is production", () => {
    process.env.NODE_ENV = "production";
    expect(getEnvironment()).toBe("production");
  });

  test("returns development by default", () => {
    process.env.NODE_ENV = undefined;
    expect(getEnvironment()).toBe("development");
  });
});

Async Testing

test("async function resolves correctly", async () => {
  const result = await fetchData();
  expect(result).toEqual({ data: "value" });
});

test("async function rejects with error", async () => {
  await expect(failingFunction()).rejects.toThrow("Error message");
});

Mocking Functions

import { vi } from "vitest";

// Mock a function
const mockCallback = vi.fn((x: number) => x * 2);
mockCallback(5);
expect(mockCallback).toHaveBeenCalledWith(5);
expect(mockCallback).toHaveReturnedWith(10);

// Spy on object method
const spy = vi.spyOn(console, "log").mockImplementation(() => {});
console.log("test");
expect(spy).toHaveBeenCalledWith("test");
spy.mockRestore();

Mocking Modules

// At top level, before imports
vi.mock("./api-client", () => ({
  fetchUser: vi.fn(() => Promise.resolve({ id: 1, name: "Test" }))
}));

import { fetchUser } from "./api-client";

test("uses mocked API", async () => {
  const user = await fetchUser();
  expect(user.name).toBe("Test");
});

Timer Mocking

import { vi } from "vitest";

test("debounced function", () => {
  vi.useFakeTimers();

  const callback = vi.fn();
  const debounced = debounce(callback, 1000);

  debounced();
  debounced();
  debounced();

  vi.advanceTimersByTime(1000);
  expect(callback).toHaveBeenCalledTimes(1);

  vi.useRealTimers();
});

Debugging Failed Tests

Reading Test Output

Focus on these signals:

  • File and line number – Where the failure occurred
  • Expected vs. received – What went wrong
  • Stack trace – Ignore framework internals, focus on your code

Common Failures

State bleeding between tests:

// Problem: Previous test left state
test("first test", () => {
  store.addItem("test");
});

test("second test", () => {
  expect(store.items).toHaveLength(0); // Fails! Still has "test"
});

// Solution: Add cleanup
afterEach(() => {
  store.clear();
});

Mock not working:

// Problem: Mock path doesn't match import
vi.mock("./utils/logger");
import { logger } from "@/utils/logger"; // Different path!

// Solution: Match exact import path
vi.mock("@/utils/logger");

Async timeout:

// Problem: Default 5s timeout too short
test("slow operation", async () => {
  await verySlowOperation(); // Times out
});

// Solution: Increase timeout
test("slow operation", async () => {
  await verySlowOperation();
}, 10000); // 10 second timeout

Debugging Tools

nlx vitest --reporter=verbose   # Detailed output
nlx vitest --ui                  # Visual debugging interface
nlx vitest --coverage            # See what's tested
nlx vitest --inspect             # Node debugger
nlx vitest --run                 # Disable watch mode

Best Practices

DO

  • Colocate tests with source files (feature.ts + feature.test.ts)
  • Use describe blocks to group related tests
  • Add afterEach() cleanup for state/mocks
  • Use visual separators for clarity (/* --- */)
  • Test behavior, not implementation
  • Use explicit type annotations for mocks
  • Keep tests focused and independent
  • Write tests before fixing bugs (reproduce the bug first)

DON’T

  • Test implementation details (internal variables)
  • Share state between tests
  • Mock everything (only mock boundaries: network, storage, time)
  • Forget to restore mocks/timers
  • Use any types in tests
  • Create brittle tests tied to DOM structure
  • Add backward-compatibility hacks for test utilities

Advanced Topics

For deeper dives, see the ./references/ directory:

  • TESTING_PATTERNS.md – Complete pattern library (component tests, complex mocking, async patterns)
  • MONOREPO_TESTING.md – Workspace-specific strategies (shared vs. app tests, path aliases, organization)
  • TROUBLESHOOTING.md – Debug guide (common errors, performance, coverage, CI/CD)

Coverage Analysis

To add coverage:

// vitest.config.ts
export default defineConfig({
  test: {
    coverage: {
      provider: "v8",
      reporter: ["text", "html", "json"],
      exclude: ["**/*.test.ts", "**/__mocks__/**", "**/node_modules/**"]
    }
  }
});

Run with: nlx vitest --coverage

Configuration Reference

Example config: vitest.config.ts

{
  environment: "jsdom",           // React/DOM APIs available
  globals: true,                  // No imports needed for describe/test/expect
  include: ["**/*.test.{js,ts,tsx}"],
  exclude: ["**/node_modules/**", "**/e2e/**"],
  setupFiles: ["./tests/setup.ts"],
  alias: {
    "@": "./src",
    // Add your project's path aliases
  },
}

Next Steps

  1. For Zustand store testing – Activate zustand skill
  2. For component testing – See ./references/TESTING_PATTERNS.md (React Testing Library setup)
  3. For monorepo-specific strategies – See ./references/MONOREPO_TESTING.md
  4. For debugging help – See ./references/TROUBLESHOOTING.md

Start with simple unit tests, add component tests as needed.