| 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
- AAA Pattern - Arrange, Act, Assert
- One assertion per test - Or logically related assertions
- Descriptive test names - Describe what is being tested
- Use beforeEach/afterEach - For setup and teardown
- Mock external dependencies - Keep tests isolated
- Test edge cases - Not just happy paths
- Avoid implementation details - Test behavior
- Keep tests fast - Mock slow operations
- Maintain 80%+ coverage - Aim high
- 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:
- references/integration-testing.md
- references/react-testing-patterns.md
- references/mocking-strategies.md
Resources
- Jest: https://jestjs.io/
- Vitest: https://vitest.dev/
- Testing Library: https://testing-library.com/