Claude Code Plugins

Community-maintained marketplace

Feedback

javascript-testing-patterns

@thapaliyabikendra/ai-artifacts
0
0

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.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name javascript-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.
layer 3
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