Claude
Skills
Sign in
โ† Back

vitest

Included with Lifetime
$97 forever

Vitest - Modern TypeScript testing framework with Vite-native performance, ESM support, and TypeScript-first design

toolchaintestingvitestvitetypescriptunit-testingcomponent-testingesm

What this skill does


# Vitest - Modern TypeScript Testing

## Overview

Vitest is a next-generation test framework powered by Vite, designed for modern TypeScript/JavaScript projects. It provides blazing-fast test execution through HMR-based test running, native ESM support, and first-class TypeScript integration.

**Key Features**:
- โšก **Vite-native**: Instant HMR-based test execution (10-100x faster than Jest)
- ๐ŸŽฏ **TypeScript-first**: Built-in TypeScript support, no configuration needed
- ๐Ÿ”„ **ESM-native**: Native ES modules, async/await, top-level await
- ๐Ÿงช **Jest-compatible**: Compatible API for easy migration
- ๐Ÿ“ธ **Snapshot testing**: Built-in snapshot support
- ๐ŸŽจ **Component testing**: React Testing Library, Vue Test Utils integration
- ๐Ÿ“Š **Coverage**: Built-in v8/c8 coverage (faster than Istanbul)
- ๐ŸŒ **UI mode**: Beautiful web UI for test debugging

**Installation**:
```bash
npm install -D vitest
# TypeScript types (usually auto-detected)
npm install -D @vitest/ui  # Optional: UI mode
```

## Basic Setup

### 1. Configure Vitest

**vitest.config.ts**:
```typescript
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,           // Use describe/it/expect globally
    environment: 'node',     // or 'jsdom' for DOM testing
    coverage: {
      provider: 'v8',        // or 'istanbul'
      reporter: ['text', 'json', 'html'],
      exclude: [
        'node_modules/',
        'dist/',
        '**/*.test.ts',
        '**/*.spec.ts',
      ],
    },
    include: ['**/*.{test,spec}.{ts,tsx}'],
    exclude: ['node_modules', 'dist', '.idea', '.git', '.cache'],
  },
});
```

### 2. TypeScript Configuration

**tsconfig.json**:
```json
{
  "compilerOptions": {
    "types": ["vitest/globals"]  // For global describe/it/expect
  }
}
```

**Alternative (without globals)**:
```typescript
import { describe, it, expect } from 'vitest';
```

### 3. Package.json Scripts

```json
{
  "scripts": {
    "test": "vitest run",              // CI mode (single run)
    "test:watch": "vitest",            // Watch mode (default)
    "test:ui": "vitest --ui",          // UI mode
    "test:coverage": "vitest run --coverage"
  }
}
```

## Core Testing Patterns

### Basic Test Structure

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

describe('Calculator', () => {
  let calculator: Calculator;

  beforeEach(() => {
    calculator = new Calculator();
  });

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

  it('handles negative numbers', () => {
    expect(calculator.add(-5, 3)).toBe(-2);
  });
});
```

### TypeScript Type Testing

```typescript
import { describe, it, expectTypeOf, assertType } from 'vitest';

interface User {
  id: number;
  name: string;
  email: string;
}

describe('Type Safety', () => {
  it('ensures correct types', () => {
    const user: User = {
      id: 1,
      name: 'Alice',
      email: '[email protected]',
    };

    // Type assertions
    expectTypeOf(user.id).toBeNumber();
    expectTypeOf(user.name).toBeString();
    expectTypeOf(user).toMatchTypeOf<User>();

    // Assert type at compile time
    assertType<User>(user);
  });

  it('checks function return types', () => {
    function getUser(): User {
      return { id: 1, name: 'Bob', email: '[email protected]' };
    }

    expectTypeOf(getUser).returns.toMatchTypeOf<User>();
  });
});
```

## Mocking and Spies

### vi.mock for Module Mocking

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

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

describe('UserService', () => {
  it('fetches user data', async () => {
    const mockUser = { id: 1, name: 'Alice' };
    vi.mocked(fetchUser).mockResolvedValue(mockUser);

    const service = new UserService();
    const user = await service.getUser(1);

    expect(fetchUser).toHaveBeenCalledWith(1);
    expect(user).toEqual(mockUser);
  });
});
```

### vi.spyOn for Method Spying

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

class Logger {
  log(message: string) {
    console.log(message);
  }
}

describe('Logger Spy', () => {
  it('tracks method calls', () => {
    const logger = new Logger();
    const spy = vi.spyOn(logger, 'log');

    logger.log('Hello');
    logger.log('World');

    expect(spy).toHaveBeenCalledTimes(2);
    expect(spy).toHaveBeenCalledWith('Hello');
    expect(spy).toHaveBeenLastCalledWith('World');

    spy.mockRestore(); // Restore original implementation
  });
});
```

### Mock Implementation

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

describe('Mock Implementation', () => {
  it('provides custom mock implementation', () => {
    const mockFn = vi.fn((x: number) => x * 2);

    expect(mockFn(5)).toBe(10);
    expect(mockFn).toHaveBeenCalledWith(5);

    // Change implementation
    mockFn.mockImplementation((x: number) => x + 10);
    expect(mockFn(5)).toBe(15);

    // One-time implementation
    mockFn.mockImplementationOnce((x: number) => 100);
    expect(mockFn(5)).toBe(100);
    expect(mockFn(5)).toBe(15); // Back to default
  });
});
```

### Mocking Timers

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

describe('Timer Mocking', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

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

  it('fast-forwards time', () => {
    const callback = vi.fn();
    setTimeout(callback, 1000);

    vi.advanceTimersByTime(500);
    expect(callback).not.toHaveBeenCalled();

    vi.advanceTimersByTime(500);
    expect(callback).toHaveBeenCalledTimes(1);
  });

  it('runs all timers', async () => {
    const callback = vi.fn();
    setTimeout(callback, 1000);
    setTimeout(callback, 2000);

    await vi.runAllTimersAsync();
    expect(callback).toHaveBeenCalledTimes(2);
  });
});
```

## React Testing Integration

### Setup React Testing Library

```bash
npm install -D @testing-library/react @testing-library/jest-dom @testing-library/user-event
npm install -D jsdom  # For DOM environment
```

**vitest.config.ts** (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',
  },
});
```

**src/test/setup.ts**:
```typescript
import '@testing-library/jest-dom';
import { expect, afterEach } from 'vitest';
import { cleanup } from '@testing-library/react';
import * as matchers from '@testing-library/jest-dom/matchers';

expect.extend(matchers);

afterEach(() => {
  cleanup();
});
```

### React Component Testing

```typescript
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Counter } from './Counter';

describe('Counter Component', () => {
  it('renders initial count', () => {
    render(<Counter initialCount={0} />);
    expect(screen.getByText('Count: 0')).toBeInTheDocument();
  });

  it('increments counter on button click', async () => {
    const user = userEvent.setup();
    render(<Counter initialCount={0} />);

    const button = screen.getByRole('button', { name: /increment/i });
    await user.click(button);

    expect(screen.getByText('Count: 1')).toBeInTheDocument();
  });

  it('calls onChange callback', async () => {
    const onChange = vi.fn();
    const user = userEvent.setup();

    render(<Counter initialCount={0} onChange={onChange} />);

    await user.click(screen.getByRole('button', { name: /increment/i }));

    expect(onChange).toHaveBeenCalledWith(1);
  });
});
```

### Testing Hooks

```typescript
import { describe, it, expect } from 'vitest';
import { renderHook, act } from '@testing-library/react';

Related in toolchain