Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
erichowens avatar

Vitest Testing Patterns

  • 287 installs
  • 178 repo stars
  • Updated July 14, 2026
  • erichowens/some_claude_skills

vitest-testing-patterns is a Claude Code skill that guides writing Vitest and React Testing Library unit, component, and integration tests with mocks, snapshots, and coverage configs.

About

vitest-testing-patterns is an erichowens/some_claude_skills entry for Vitest and React Testing Library test authoring. It activates for unit tests, component tests, integration tests, dependency mocking, snapshot patterns, coverage setup, and CI-friendly npm workflows. Allowed tools include Read, Write, Edit, and Bash for npm and npx commands. The skill pairs with test-automation-expert for broader strategy and playwright-e2e-tester for pyramid complement. Reach for it when creating or refactoring test files in TypeScript or JavaScript React codebases rather than manual Jest migrations without patterns.

  • Vitest config and workspace setup
  • Mock and spy patterns for modules
  • Component and hook test examples
  • Coverage thresholds in CI
  • Snapshot and async test hygiene

Vitest Testing Patterns by the numbers

  • 287 all-time installs (skills.sh)
  • Ranked #720 of 2,153 Testing & QA skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/erichowens/some_claude_skills --skill vitest-testing-patterns

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs287
repo stars178
Last updatedJuly 14, 2026
Repositoryerichowens/some_claude_skills

How do you write Vitest tests with React Testing Library?

Write fast unit and integration tests with Vitest mocks, snapshots, coverage, and CI-friendly configs for TypeScript JavaScript codebases.

Who is it for?

Frontend developers shipping TypeScript or JavaScript React apps who standardize on Vitest for fast unit and component tests.

Skip if: Teams standardized on Playwright-only E2E coverage or backend suites that never touch React components.

When should I use this skill?

User creates test files, asks for Vitest mocks, snapshots, coverage, or React Testing Library component tests.

What you get

Vitest test files, mock setups, snapshot tests, and coverage-ready npm test configuration.

  • Vitest test files
  • Mock and snapshot patterns
  • Coverage configuration

By the numbers

  • Allowed tools include Bash for npm and npx commands
  • Pairs with playwright-e2e-tester and test-automation-expert skills

Files

SKILL.mdMarkdownGitHub ↗

Vitest Testing Patterns

This skill helps you write effective tests using Vitest and React Testing Library following project conventions.

When to Use

USE this skill for:

  • Writing unit tests for utilities and functions
  • Creating component tests with React Testing Library
  • Setting up mocks for API calls, databases, or external services
  • Integration testing patterns
  • Understanding test coverage and CI setup

DO NOT use for:

  • Jest-specific patterns → similar but check Jest docs for differences
  • End-to-end testing → use Playwright or Cypress skills
  • Performance testing → use dedicated performance tools
  • API contract testing → use OpenAPI/Pact patterns

Test Infrastructure

Configuration: vitest.config.ts

  • Environment: jsdom
  • Setup file: src/test/setup.ts
  • Coverage: v8 provider

Commands:

npm test              # Watch mode
npm run test:run      # Single run
npm run test:coverage # With coverage

File Organization

src/
├── app/api/__tests__/        # API route tests
├── components/__tests__/     # Component tests
├── lib/__tests__/            # Library/utility tests
└── lib/{feature}/__tests__/  # Feature-specific tests

Name tests as {name}.test.ts or {name}.test.tsx.

Core Testing Patterns

1. API Route Tests

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GET, POST } from '../route';
import { NextRequest } from 'next/server';

// Mock dependencies
vi.mock('@/lib/auth', () => ({
  getSession: vi.fn(),
}));

vi.mock('@/db', () => ({
  db: {
    select: vi.fn().mockReturnThis(),
    from: vi.fn().mockReturnThis(),
    where: vi.fn().mockResolvedValue([]),
  },
}));

describe('GET /api/feature', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('returns 401 when not authenticated', async () => {
    vi.mocked(getSession).mockResolvedValue(null);

    const request = new NextRequest('http://localhost/api/feature');
    const response = await GET(request);

    expect(response.status).toBe(401);
  });

  it('returns data when authenticated', async () => {
    vi.mocked(getSession).mockResolvedValue({ userId: 'user-123' });
    vi.mocked(db.select).mockReturnValue({
      from: vi.fn().mockReturnValue({
        where: vi.fn().mockResolvedValue([{ id: '1', name: 'Test' }]),
      }),
    });

    const request = new NextRequest('http://localhost/api/feature');
    const response = await GET(request);
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data).toHaveLength(1);
  });
});

2. Component Tests

import { describe, it, expect, vi } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { FeatureComponent } from '../FeatureComponent';

// Mock hooks
vi.mock('@/hooks/useAuth', () => ({
  useAuth: vi.fn().mockReturnValue({
    user: { id: 'user-123', name: 'Test User' },
    isLoading: false,
  }),
}));

describe('FeatureComponent', () => {
  it('renders loading state', () => {
    vi.mocked(useAuth).mockReturnValueOnce({
      user: null,
      isLoading: true,
    });

    render(<FeatureComponent />);
    expect(screen.getByText(/loading/i)).toBeInTheDocument();
  });

  it('handles user interaction', async () => {
    const user = userEvent.setup();
    const onSubmit = vi.fn();

    render(<FeatureComponent onSubmit={onSubmit} />);

    await user.type(screen.getByRole('textbox'), 'Test input');
    await user.click(screen.getByRole('button', { name: /submit/i }));

    expect(onSubmit).toHaveBeenCalledWith('Test input');
  });

  it('displays error state', async () => {
    vi.mocked(fetch).mockRejectedValueOnce(new Error('Network error'));

    render(<FeatureComponent />);

    await waitFor(() => {
      expect(screen.getByRole('alert')).toHaveTextContent(/error/i);
    });
  });
});

3. Library/Utility Tests

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { processData, formatDate } from '../utils';

describe('processData', () => {
  it('transforms input correctly', () => {
    const input = { raw: 'data' };
    const result = processData(input);

    expect(result).toEqual({
      processed: true,
      data: 'DATA',
    });
  });

  it('throws on invalid input', () => {
    expect(() => processData(null)).toThrow('Invalid input');
  });
});

describe('formatDate', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date('2025-01-15T10:00:00Z'));
  });

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

  it('formats relative dates', () => {
    const yesterday = new Date('2025-01-14T10:00:00Z');
    expect(formatDate(yesterday)).toBe('yesterday');
  });
});

Mocking Patterns

Module Mocking

// Mock entire module
vi.mock('@/lib/auth', () => ({
  getSession: vi.fn(),
  requireAuth: vi.fn(),
}));

// Mock with partial implementation
vi.mock('date-fns', async () => {
  const actual = await vi.importActual('date-fns');
  return {
    ...actual,
    format: vi.fn(() => '2025-01-15'),
  };
});

// Mock default export (like Anthropic SDK)
vi.mock('@anthropic-ai/sdk', () => ({
  default: class MockAnthropic {
    messages = {
      create: vi.fn().mockResolvedValue({
        content: [{ type: 'text', text: 'Mock response' }],
        usage: { input_tokens: 10, output_tokens: 20 },
      }),
    };
  },
}));

Function Mocking

// Create mock function
const mockFn = vi.fn();

// Set return values
mockFn.mockReturnValue('sync value');
mockFn.mockResolvedValue('async value');
mockFn.mockRejectedValue(new Error('Failed'));

// One-time behavior
mockFn.mockReturnValueOnce('first call only');

// Custom implementation
mockFn.mockImplementation((arg) => arg.toUpperCase());

// Verify calls
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith('expected', 'args');

Chained Mock Pattern (Drizzle ORM)

vi.mock('@/db', () => ({
  db: {
    select: vi.fn().mockReturnValue({
      from: vi.fn().mockReturnValue({
        where: vi.fn().mockReturnValue({
          orderBy: vi.fn().mockReturnValue({
            limit: vi.fn().mockResolvedValue([{ id: '1' }]),
          }),
        }),
      }),
    }),
    insert: vi.fn().mockReturnValue({
      values: vi.fn().mockReturnValue({
        returning: vi.fn().mockResolvedValue([{ id: 'new-1' }]),
      }),
    }),
  },
}));

Timer Mocking

describe('debounced function', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

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

  it('debounces calls', async () => {
    const callback = vi.fn();
    const debounced = debounce(callback, 300);

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

    expect(callback).not.toHaveBeenCalled();

    vi.advanceTimersByTime(300);

    expect(callback).toHaveBeenCalledTimes(1);
  });
});

Query Priorities

Use queries in this order (most to least preferred):

1. getByRole - Accessible queries (buttons, links, headings) 2. getByLabelText - Form fields with labels 3. getByPlaceholderText - Inputs with placeholders 4. getByText - Non-interactive elements 5. getByTestId - Last resort (data-testid)

// Preferred
screen.getByRole('button', { name: /submit/i });
screen.getByRole('heading', { level: 1 });
screen.getByLabelText(/email/i);

// Avoid unless necessary
screen.getByTestId('submit-button');

Async Patterns

// Wait for element to appear
await waitFor(() => {
  expect(screen.getByText('Loaded')).toBeInTheDocument();
});

// Find (built-in waitFor)
const element = await screen.findByText('Loaded');

// Wait for element to disappear
await waitFor(() => {
  expect(screen.queryByText('Loading')).not.toBeInTheDocument();
});

Test Cleanup

import { cleanup } from '@testing-library/react';

afterEach(() => {
  cleanup();            // React cleanup (automatic with setup.ts)
  vi.clearAllMocks();   // Reset mock call counts
  vi.resetAllMocks();   // Reset mocks to initial state
  vi.restoreAllMocks(); // Restore original implementations
});

Accessibility Testing

import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

it('has no accessibility violations', async () => {
  const { container } = render(<Component />);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

Common Matchers

// jest-dom matchers (from setup.ts)
expect(element).toBeInTheDocument();
expect(element).toBeVisible();
expect(element).toBeDisabled();
expect(element).toHaveTextContent('text');
expect(element).toHaveAttribute('href', '/path');
expect(element).toHaveClass('active');
expect(input).toHaveValue('input value');

References

Related skills

How it compares

Use vitest-testing-patterns for fast unit and component layers; pair with Playwright skills for end-to-end browser coverage.

FAQ

What testing stack does vitest-testing-patterns use?

vitest-testing-patterns guides Vitest with React Testing Library for unit, component, and integration tests. It covers mocks, snapshots, coverage, and npm workflows in TypeScript or JavaScript codebases.

When should I invoke vitest-testing-patterns?

Invoke vitest-testing-patterns when creating or editing test files, setting up mocks or snapshots, or configuring Vitest coverage. Use Playwright skills instead for browser E2E-only work.

Testing & QAtestingfrontendbackend

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.