vitest

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Vitest - Modern Test Framework

Vitest - 现代化测试框架

Status: Production Ready Last Updated: 2026-02-06 Vitest Version: 4.x Vite Compatibility: 6.x

状态:生产可用 最后更新:2026-02-06 Vitest版本:4.x Vite兼容性:6.x

Quick Start

快速开始

bash
undefined
bash
undefined

Install

安装

pnpm add -D vitest
pnpm add -D vitest

Add to package.json

添加至package.json

{ "scripts": { "test": "vitest", "test:run": "vitest run", "test:coverage": "vitest run --coverage" } }

---
{ "scripts": { "test": "vitest", "test:run": "vitest run", "test:coverage": "vitest run --coverage" } }

---

Configuration

配置

Minimal Config (vitest.config.ts)

最简配置(vitest.config.ts)

typescript
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
  },
});
typescript
import { defineConfig } from 'vitest/config';

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

React Config

React配置

typescript
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: ['./src/test/setup.ts'],
    css: true,
  },
});
typescript
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: ['./src/test/setup.ts'],
    css: true,
  },
});

Cloudflare Workers Config

Cloudflare Workers配置

typescript
import { defineConfig } from 'vitest/config';
import { cloudflare } from '@cloudflare/vite-plugin';

export default defineConfig({
  plugins: [cloudflare()],
  test: {
    globals: true,
    environment: 'node',
    // Workers tests often need longer timeouts for D1/KV
    testTimeout: 10000,
  },
});

typescript
import { defineConfig } from 'vitest/config';
import { cloudflare } from '@cloudflare/vite-plugin';

export default defineConfig({
  plugins: [cloudflare()],
  test: {
    globals: true,
    environment: 'node',
    // Workers测试通常需要为D1/KV设置更长的超时时间
    testTimeout: 10000,
  },
});

Mocking Patterns

Mock模式

vi.mock - Module Mocking

vi.mock - 模块Mock

typescript
import { vi, describe, it, expect } from 'vitest';
import { fetchUser } from './api';

// Mock entire module
vi.mock('./api', () => ({
  fetchUser: vi.fn(),
}));

describe('User component', () => {
  it('fetches user data', async () => {
    // Type-safe mock implementation
    vi.mocked(fetchUser).mockResolvedValue({ id: 1, name: 'Test' });

    // ... test code

    expect(fetchUser).toHaveBeenCalledWith(1);
  });
});
typescript
import { vi, describe, it, expect } from 'vitest';
import { fetchUser } from './api';

// Mock整个模块
vi.mock('./api', () => ({
  fetchUser: vi.fn(),
}));

describe('User组件', () => {
  it('获取用户数据', async () => {
    // 类型安全的Mock实现
    vi.mocked(fetchUser).mockResolvedValue({ id: 1, name: 'Test' });

    // ... 测试代码

    expect(fetchUser).toHaveBeenCalledWith(1);
  });
});

vi.spyOn - Spy on Methods

vi.spyOn - 方法监听

typescript
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';

describe('Date handling', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date('2026-01-01'));
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it('uses mocked date', () => {
    expect(new Date().getFullYear()).toBe(2026);
  });
});
typescript
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';

describe('日期处理', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date('2026-01-01'));
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it('使用模拟日期', () => {
    expect(new Date().getFullYear()).toBe(2026);
  });
});

vi.stubGlobal - Global Mocks

vi.stubGlobal - 全局Mock

typescript
import { vi, describe, it, expect } from 'vitest';

describe('Environment', () => {
  it('mocks fetch globally', async () => {
    const mockFetch = vi.fn().mockResolvedValue({
      ok: true,
      json: () => Promise.resolve({ data: 'test' }),
    });

    vi.stubGlobal('fetch', mockFetch);

    const response = await fetch('/api/test');
    expect(mockFetch).toHaveBeenCalledWith('/api/test');

    vi.unstubAllGlobals();
  });
});

typescript
import { vi, describe, it, expect } from 'vitest';

describe('环境变量', () => {
  it('全局Mock fetch', async () => {
    const mockFetch = vi.fn().mockResolvedValue({
      ok: true,
      json: () => Promise.resolve({ data: 'test' }),
    });

    vi.stubGlobal('fetch', mockFetch);

    const response = await fetch('/api/test');
    expect(mockFetch).toHaveBeenCalledWith('/api/test');

    vi.unstubAllGlobals();
  });
});

Snapshot Testing

快照测试

Basic Snapshots

基础快照

typescript
import { describe, it, expect } from 'vitest';

describe('Component output', () => {
  it('matches snapshot', () => {
    const result = renderComponent({ title: 'Hello' });
    expect(result).toMatchSnapshot();
  });

  it('matches inline snapshot', () => {
    const result = { name: 'test', count: 42 };
    expect(result).toMatchInlineSnapshot(`
      {
        "count": 42,
        "name": "test",
      }
    `);
  });
});
typescript
import { describe, it, expect } from 'vitest';

describe('组件输出', () => {
  it('匹配快照', () => {
    const result = renderComponent({ title: 'Hello' });
    expect(result).toMatchSnapshot();
  });

  it('匹配内联快照', () => {
    const result = { name: 'test', count: 42 };
    expect(result).toMatchInlineSnapshot(`
      {
        "count": 42,
        "name": "test",
      }
    `);
  });
});

Update Snapshots

更新快照

bash
undefined
bash
undefined

Update all snapshots

更新所有快照

vitest run --update
vitest run --update

Interactive update

交互式更新

vitest --ui

---
vitest --ui

---

In-Source Testing

源码内测试

Test code directly in source files (tree-shaken in production):
typescript
// src/utils/math.ts
export function add(a: number, b: number): number {
  return a + b;
}

// In-source test block
if (import.meta.vitest) {
  const { describe, it, expect } = import.meta.vitest;

  describe('add', () => {
    it('adds two numbers', () => {
      expect(add(1, 2)).toBe(3);
    });
  });
}
Config for in-source testing:
typescript
// vitest.config.ts
export default defineConfig({
  test: {
    includeSource: ['src/**/*.{js,ts}'],
  },
  define: {
    'import.meta.vitest': 'undefined', // Tree-shake in production
  },
});

直接在源码文件中编写测试代码(生产环境下会被摇树优化移除):
typescript
// src/utils/math.ts
export function add(a: number, b: number): number {
  return a + b;
}

// 源码内测试块
if (import.meta.vitest) {
  const { describe, it, expect } = import.meta.vitest;

  describe('add', () => {
    it('两数相加', () => {
      expect(add(1, 2)).toBe(3);
    });
  });
}
源码内测试配置:
typescript
// vitest.config.ts
export default defineConfig({
  test: {
    includeSource: ['src/**/*.{js,ts}'],
  },
  define: {
    'import.meta.vitest': 'undefined', // 生产环境下摇树移除
  },
});

Workspace Configuration (Monorepos)

工作区配置(单体仓库)

typescript
// vitest.workspace.ts
import { defineWorkspace } from 'vitest/config';

export default defineWorkspace([
  // Each package can have its own config
  'packages/*/vitest.config.ts',

  // Or define inline
  {
    test: {
      name: 'unit',
      include: ['src/**/*.test.ts'],
      environment: 'node',
    },
  },
  {
    test: {
      name: 'browser',
      include: ['src/**/*.browser.test.ts'],
      browser: {
        enabled: true,
        provider: 'playwright',
        name: 'chromium',
      },
    },
  },
]);

typescript
// vitest.workspace.ts
import { defineWorkspace } from 'vitest/config';

export default defineWorkspace([
  // 每个包可拥有独立配置
  'packages/*/vitest.config.ts',

  // 或内联定义
  {
    test: {
      name: 'unit',
      include: ['src/**/*.test.ts'],
      environment: 'node',
    },
  },
  {
    test: {
      name: 'browser',
      include: ['src/**/*.browser.test.ts'],
      browser: {
        enabled: true,
        provider: 'playwright',
        name: 'chromium',
      },
    },
  },
]);

Browser Mode Testing

浏览器模式测试

typescript
// vitest.config.ts
export default defineConfig({
  test: {
    browser: {
      enabled: true,
      provider: 'playwright', // or 'webdriverio'
      name: 'chromium',
      headless: true,
    },
  },
});
bash
undefined
typescript
// vitest.config.ts
export default defineConfig({
  test: {
    browser: {
      enabled: true,
      provider: 'playwright', // 或 'webdriverio'
      name: 'chromium',
      headless: true,
    },
  },
});
bash
undefined

Install browser provider

安装浏览器提供者

pnpm add -D @vitest/browser playwright

---
pnpm add -D @vitest/browser playwright

---

Coverage

覆盖率

bash
undefined
bash
undefined

Install coverage provider

安装覆盖率提供者

pnpm add -D @vitest/coverage-v8
pnpm add -D @vitest/coverage-v8

Run with coverage

带覆盖率运行测试

vitest run --coverage

```typescript
// vitest.config.ts
export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html', 'lcov'],
      exclude: [
        'node_modules/',
        'src/test/',
        '**/*.d.ts',
      ],
      thresholds: {
        statements: 80,
        branches: 80,
        functions: 80,
        lines: 80,
      },
    },
  },
});

vitest run --coverage

```typescript
// vitest.config.ts
export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html', 'lcov'],
      exclude: [
        'node_modules/',
        'src/test/',
        '**/*.d.ts',
      ],
      thresholds: {
        statements: 80,
        branches: 80,
        functions: 80,
        lines: 80,
      },
    },
  },
});

Jest Migration

Jest迁移

Key Differences

核心差异

JestVitest
jest.fn()
vi.fn()
jest.mock()
vi.mock()
jest.spyOn()
vi.spyOn()
jest.useFakeTimers()
vi.useFakeTimers()
jest.clearAllMocks()
vi.clearAllMocks()
@jest/globals
vitest
JestVitest
jest.fn()
vi.fn()
jest.mock()
vi.mock()
jest.spyOn()
vi.spyOn()
jest.useFakeTimers()
vi.useFakeTimers()
jest.clearAllMocks()
vi.clearAllMocks()
@jest/globals
vitest

Migration Steps

迁移步骤

  1. Replace imports:
typescript
// Before (Jest)
import { jest } from '@jest/globals';

// After (Vitest)
import { vi } from 'vitest';
  1. Update config:
typescript
// jest.config.js → vitest.config.ts
export default defineConfig({
  test: {
    globals: true, // Enables describe/it/expect without imports
    environment: 'jsdom',
  },
});
  1. Replace jest. with vi.:
bash
undefined
  1. 替换导入:
typescript
// 之前(Jest)
import { jest } from '@jest/globals';

// 之后(Vitest)
import { vi } from 'vitest';
  1. 更新配置:
typescript
// jest.config.js → vitest.config.ts
export default defineConfig({
  test: {
    globals: true, // 无需导入即可使用describe/it/expect
    environment: 'jsdom',
  },
});
  1. 替换jest.为vi.:
bash
undefined

Quick replace (review changes carefully)

快速替换(请仔细检查变更)

find src -name "*.test.ts" -exec sed -i 's/jest./vi./g' {} ;

---
find src -name "*.test.ts" -exec sed -i 's/jest./vi./g' {} ;

---

Common Patterns

常见模式

Testing Async Code

异步代码测试

typescript
import { describe, it, expect } from 'vitest';

describe('async operations', () => {
  it('resolves promise', async () => {
    const result = await fetchData();
    expect(result).toBeDefined();
  });

  it('rejects with error', async () => {
    await expect(failingOperation()).rejects.toThrow('Expected error');
  });
});
typescript
import { describe, it, expect } from 'vitest';

describe('异步操作', () => {
  it('解析Promise', async () => {
    const result = await fetchData();
    expect(result).toBeDefined();
  });

  it('抛出指定错误', async () => {
    await expect(failingOperation()).rejects.toThrow('预期错误');
  });
});

Testing with Fixtures

测试夹具使用

typescript
import { describe, it, expect, beforeEach } from 'vitest';

describe('with fixtures', () => {
  let testData: TestData;

  beforeEach(() => {
    testData = createTestFixture();
  });

  it('uses fixture', () => {
    expect(testData.id).toBeDefined();
  });
});
typescript
import { describe, it, expect, beforeEach } from 'vitest';

describe('使用夹具', () => {
  let testData: TestData;

  beforeEach(() => {
    testData = createTestFixture();
  });

  it('使用夹具数据', () => {
    expect(testData.id).toBeDefined();
  });
});

Parameterized Tests

参数化测试

typescript
import { describe, it, expect } from 'vitest';

describe.each([
  { input: 1, expected: 2 },
  { input: 2, expected: 4 },
  { input: 3, expected: 6 },
])('double($input)', ({ input, expected }) => {
  it(`returns ${expected}`, () => {
    expect(double(input)).toBe(expected);
  });
});

typescript
import { describe, it, expect } from 'vitest';

describe.each([
  { input: 1, expected: 2 },
  { input: 2, expected: 4 },
  { input: 3, expected: 6 },
])('翻倍($input)', ({ input, expected }) => {
  it(`返回${expected}`, () => {
    expect(double(input)).toBe(expected);
  });
});

Debugging

调试

Run Single Test

运行单个测试

bash
vitest run -t "test name"
vitest run src/specific.test.ts
bash
vitest run -t "测试名称"
vitest run src/specific.test.ts

Debug Mode

调试模式

bash
undefined
bash
undefined

With Node inspector

使用Node调试器

node --inspect-brk ./node_modules/vitest/vitest.mjs run
node --inspect-brk ./node_modules/vitest/vitest.mjs run

Or use Vitest UI

或使用Vitest UI

vitest --ui
undefined
vitest --ui
undefined

Watch Mode

监听模式

bash
vitest          # Watch mode (default)
vitest run      # Single run
vitest watch    # Explicit watch

bash
vitest          # 默认监听模式
vitest run      # 单次运行
vitest watch    # 显式开启监听

Troubleshooting

问题排查

"Cannot find module" in mocks

Mock中出现“无法找到模块”

typescript
// Ensure mock path matches import path exactly
vi.mock('./api'); // Matches: import { x } from './api'
vi.mock('../api'); // Different! Won't work for './api' imports
typescript
// 确保Mock路径与导入路径完全匹配
vi.mock('./api'); // 匹配:import { x } from './api'
vi.mock('../api'); // 不同!无法匹配'./api'的导入

ESM/CJS Issues

ESM/CJS兼容问题

typescript
// vitest.config.ts - for CJS dependencies
export default defineConfig({
  test: {
    deps: {
      inline: ['problematic-cjs-package'],
    },
  },
});
typescript
// vitest.config.ts - 针对CJS依赖
export default defineConfig({
  test: {
    deps: {
      inline: ['problematic-cjs-package'],
    },
  },
});

Globals Not Defined

全局变量未定义

typescript
// If using globals: true but TypeScript complains
// Add to tsconfig.json:
{
  "compilerOptions": {
    "types": ["vitest/globals"]
  }
}

typescript
// 若启用globals: true但TypeScript报错
// 添加至tsconfig.json:
{
  "compilerOptions": {
    "types": ["vitest/globals"]
  }
}

See Also

相关链接

  • testing-patterns
    skill - General testing patterns
  • testing-library
    skill - React Testing Library integration
  • Official docs: https://vitest.dev
  • testing-patterns
    技能 - 通用测试模式
  • testing-library
    技能 - React Testing Library集成
  • 官方文档:https://vitest.dev