javascript-testing-patterns

thapaliyabikendra's avatarfrom thapaliyabikendra

Implement comprehensive testing strategies using Jest, Vitest, and Testing Library for unit tests, integration tests, and end-to-end testing with mocking, fixtures, and test-driven development. Use when writing JavaScript/TypeScript tests, setting up test infrastructure, or implementing TDD/BDD workflows.

⭐0stars🔀0forks📁View on GitHub🕐Updated Dec 19, 2025

When & Why to Use This Skill

This Claude skill provides comprehensive patterns and strategies for implementing robust testing in JavaScript and TypeScript applications. It covers everything from initial framework setup with Jest and Vitest to advanced mocking techniques, React component testing, and Test-Driven Development (TDD) workflows, ensuring high code quality and reliability through industry-standard best practices.

Use Cases

  • Setting up a professional testing infrastructure for a new or existing TypeScript project using Vitest or Jest.
  • Writing unit tests for complex business logic, including pure functions, class-based services, and asynchronous operations.
  • Implementing advanced mocking strategies for external API calls, database dependencies, and third-party modules to ensure isolated test environments.
  • Testing React UI components and custom hooks using Testing Library to verify user interactions and state changes.
  • Adopting Test-Driven Development (TDD) or Behavior-Driven Development (BDD) workflows to improve software design and catch regressions early.
  • Configuring automated code coverage reports and setting quality thresholds to maintain high testing standards across a codebase.
namejavascript-testing-patterns
description"Implement comprehensive testing strategies using Jest, Vitest, and Testing Library for unit tests, integration tests, and end-to-end testing with mocking, fixtures, and test-driven development. Use when writing JavaScript/TypeScript tests, setting up test infrastructure, or implementing TDD/BDD workflows."
layer3
tech_stack[typescript, javascript, jest, vitest]
topics[jest, vitest, testing-library, mocking, tdd, react-testing]
depends_on[modern-javascript-patterns]
complements[e2e-testing-patterns]
keywords[Jest, Vitest, describe, it, expect, mock, render, screen, fireEvent]

JavaScript Testing Patterns

Comprehensive testing strategies for JavaScript/TypeScript applications.

Framework Setup

Jest Configuration

// jest.config.ts
import type { Config } from 'jest';

const config: Config = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['<rootDir>/src'],
  testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
  collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts'],
  coverageThreshold: {
    global: { branches: 80, functions: 80, lines: 80, statements: 80 },
  },
  setupFilesAfterEnv: ['<rootDir>/src/test/setup.ts'],
};

export default config;

Vitest Configuration

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
    },
    setupFiles: ['./src/test/setup.ts'],
  },
});

Unit Testing Patterns

Testing Pure Functions

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

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

    it('should handle zero', () => {
      expect(add(0, 5)).toBe(5);
    });
  });

  describe('divide', () => {
    it('should throw error when dividing by zero', () => {
      expect(() => divide(10, 0)).toThrow('Division by zero');
    });
  });
});

Testing Classes

describe('UserService', () => {
  let service: UserService;

  beforeEach(() => {
    service = new UserService();
  });

  describe('create', () => {
    it('should create a new user', () => {
      const user = { id: '1', name: 'John', email: 'john@example.com' };
      const created = service.create(user);

      expect(created).toEqual(user);
      expect(service.findById('1')).toEqual(user);
    });

    it('should throw error if user already exists', () => {
      const user = { id: '1', name: 'John', email: 'john@example.com' };
      service.create(user);
      expect(() => service.create(user)).toThrow('User already exists');
    });
  });
});

Testing Async Functions

describe('ApiService', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('should fetch user successfully', async () => {
    const mockUser = { id: '1', name: 'John' };
    global.fetch = vi.fn().mockResolvedValueOnce({
      ok: true,
      json: async () => mockUser,
    });

    const user = await service.fetchUser('1');

    expect(user).toEqual(mockUser);
    expect(fetch).toHaveBeenCalledWith('https://api.example.com/users/1');
  });

  it('should throw error if user not found', async () => {
    global.fetch = vi.fn().mockResolvedValueOnce({ ok: false });
    await expect(service.fetchUser('999')).rejects.toThrow('User not found');
  });
});

Mocking Patterns

Mocking Modules

vi.mock('nodemailer', () => ({
  default: {
    createTransport: vi.fn(() => ({
      sendMail: vi.fn().mockResolvedValue({ messageId: '123' }),
    })),
  },
}));

Dependency Injection

describe('UserService', () => {
  let service: UserService;
  let mockRepository: IUserRepository;

  beforeEach(() => {
    mockRepository = {
      findById: vi.fn(),
      create: vi.fn(),
    };
    service = new UserService(mockRepository);
  });

  it('should return user if found', async () => {
    const mockUser = { id: '1', name: 'John' };
    vi.mocked(mockRepository.findById).mockResolvedValue(mockUser);

    const user = await service.getUser('1');

    expect(user).toEqual(mockUser);
    expect(mockRepository.findById).toHaveBeenCalledWith('1');
  });
});

Spying on Functions

let loggerSpy: any;

beforeEach(() => {
  loggerSpy = vi.spyOn(logger, 'info');
});

afterEach(() => {
  loggerSpy.mockRestore();
});

it('should log order processing', async () => {
  await service.processOrder('123');
  expect(loggerSpy).toHaveBeenCalledWith('Processing order 123');
});

React Component Testing

import { render, screen, fireEvent } from '@testing-library/react';

describe('UserForm', () => {
  it('should render form inputs', () => {
    render(<UserForm onSubmit={vi.fn()} />);

    expect(screen.getByPlaceholderText('Name')).toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Submit' })).toBeInTheDocument();
  });

  it('should call onSubmit with form data', () => {
    const onSubmit = vi.fn();
    render(<UserForm onSubmit={onSubmit} />);

    fireEvent.change(screen.getByTestId('name-input'), {
      target: { value: 'John Doe' },
    });
    fireEvent.click(screen.getByRole('button', { name: 'Submit' }));

    expect(onSubmit).toHaveBeenCalledWith({ name: 'John Doe' });
  });
});

Testing Hooks

import { renderHook, act } from '@testing-library/react';

describe('useCounter', () => {
  it('should increment count', () => {
    const { result } = renderHook(() => useCounter());

    act(() => {
      result.current.increment();
    });

    expect(result.current.count).toBe(1);
  });
});

Test Fixtures

import { faker } from '@faker-js/faker';

export function createUserFixture(overrides?: Partial<User>): User {
  return {
    id: faker.string.uuid(),
    name: faker.person.fullName(),
    email: faker.internet.email(),
    ...overrides,
  };
}

Best Practices

  1. AAA Pattern - Arrange, Act, Assert
  2. One assertion per test - Or logically related assertions
  3. Descriptive test names - Describe what is being tested
  4. Use beforeEach/afterEach - For setup and teardown
  5. Mock external dependencies - Keep tests isolated
  6. Test edge cases - Not just happy paths
  7. Avoid implementation details - Test behavior
  8. Keep tests fast - Mock slow operations
  9. Maintain 80%+ coverage - Aim high
  10. Clean up after tests - Prevent pollution

Common Patterns

// Testing promises
await expect(service.fetchUser('invalid')).rejects.toThrow('Not found');

// Testing timers
vi.useFakeTimers();
vi.advanceTimersByTime(1000);
vi.useRealTimers();

// Snapshot testing
expect(container.firstChild).toMatchSnapshot();

Detailed References

For comprehensive patterns, see:

Resources