Vitest Introduction - Fast Unit Testing

intermediate | 40 min read | 2025.12.09

What You’ll Learn in This Tutorial

✓ Vitest setup
✓ Writing basic tests
✓ Mocking
✓ Coverage
✓ UI mode

Step 1: Setup

npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'jsdom',
    globals: true,
    setupFiles: './src/test/setup.ts',
  },
});
// src/test/setup.ts
import '@testing-library/jest-dom';

Step 2: Basic Tests

// src/utils/math.ts
export function add(a: number, b: number) {
  return a + b;
}

// src/utils/math.test.ts
import { describe, it, expect } from 'vitest';
import { add } from './math';

describe('add', () => {
  it('should add two numbers', () => {
    expect(add(1, 2)).toBe(3);
  });

  it('should handle negative numbers', () => {
    expect(add(-1, 1)).toBe(0);
  });
});

Step 3: Component Tests

// src/components/Button.tsx
interface Props {
  onClick: () => void;
  children: React.ReactNode;
}

export function Button({ onClick, children }: Props) {
  return <button onClick={onClick}>{children}</button>;
}

// src/components/Button.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import { Button } from './Button';

describe('Button', () => {
  it('renders children', () => {
    render(<Button onClick={() => {}}>Click me</Button>);
    expect(screen.getByText('Click me')).toBeInTheDocument();
  });

  it('calls onClick when clicked', async () => {
    const handleClick = vi.fn();
    render(<Button onClick={handleClick}>Click me</Button>);

    await userEvent.click(screen.getByRole('button'));
    expect(handleClick).toHaveBeenCalledTimes(1);
  });
});

Step 4: Mocking

// Module mock
vi.mock('./api', () => ({
  fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Test' }),
}));

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

// Spy
const spy = vi.spyOn(console, 'log');
console.log('test');
expect(spy).toHaveBeenCalledWith('test');

Step 5: Async Tests

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

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

  it('handles promises', () => {
    return expect(asyncFn()).resolves.toBe('value');
  });

  it('handles rejections', () => {
    return expect(failingFn()).rejects.toThrow('error');
  });
});

Step 6: Running Tests

# Run tests
npm run test

# Watch mode
npm run test -- --watch

# UI mode
npm run test -- --ui

# Coverage
npm run test -- --coverage

Step 7: package.json Scripts

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

Summary

Vitest is a fast testing framework integrated with Vite. With Jest-compatible APIs for smooth migration and UI mode for efficient debugging.

← Back to list