| name | jest |
| description | JavaScript testing with Jest — unit tests, mocks, spies, snapshot testing, code coverage, and configuration. Use when writing or running JavaScript/TypeScript tests, setting up a test suite with Jest, debugging failing tests, mocking modules or functions, measuring coverage, or configuring Jest in jest.config.*. Pairs with test-driven-development for the workflow/methodology. Works with React (Testing Library), Node.js, and any JS/TS project. |
Jest — JavaScript Testing
Overview
Jest is the de-facto JavaScript/TypeScript test framework: zero-config setup, built-in mocking, snapshot testing, and parallel test execution. This skill is the tool reference; for the red-green-refactor methodology see [[test-driven-development]].
Installation
npm install --save-dev jest
npm install --save-dev jest @types/jest ts-jest
npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event
Configuration
export default {
testEnvironment: 'node',
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
collectCoverageFrom: ['src/**/*.{js,ts,tsx}'],
coverageThreshold: { global: { lines: 80 } },
};
Add to package.json:
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}
Running Tests
jest
jest --watch
jest --coverage
jest path/to/test.spec.ts
jest -t "user login"
jest --bail
jest --verbose
Test Structure
import { createUser } from './user';
describe('createUser', () => {
it('creates a user with a name', () => {
const user = createUser('Alice');
expect(user.name).toBe('Alice');
});
test('throws when name is empty', () => {
expect(() => createUser('')).toThrow('Name required');
});
});
Common Matchers
expect(value).toBe(42);
expect(obj).toEqual({ a: 1 });
expect(str).toMatch(/pattern/);
expect(arr).toContain('item');
expect(fn).toThrow(Error);
expect(value).toBeDefined();
expect(value).toBeNull();
expect(num).toBeGreaterThan(0);
expect(arr).toHaveLength(3);
expect(obj).toHaveProperty('key', 'value');
Mocking
jest.mock('./api');
import { fetchUser } from './api';
(fetchUser as jest.Mock).mockResolvedValue({ id: 1, name: 'Alice' });
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
expect(consoleSpy).toHaveBeenCalledWith('error message');
consoleSpy.mockRestore();
const mockFn = jest.fn()
.mockReturnValueOnce('first')
.mockReturnValue('default');
const mockFetch = jest.fn().mockResolvedValue({ data: 'value' });
const mockFetchFail = jest.fn().mockRejectedValue(new Error('Network error'));
Setup & Teardown
beforeAll(() => { });
afterAll(() => { });
beforeEach(() => { });
afterEach(() => { });
beforeEach(async () => {
await db.connect();
});
afterEach(async () => {
await db.disconnect();
});
Snapshot Testing
it('renders correctly', () => {
const { asFragment } = render(<Button label="Click me" />);
expect(asFragment()).toMatchSnapshot();
});
jest --updateSnapshot
Async Tests
it('fetches user data', async () => {
const user = await fetchUser(1);
expect(user.name).toBe('Alice');
});
it('resolves with data', () => {
return expect(fetchUser(1)).resolves.toMatchObject({ name: 'Alice' });
});
it('rejects on not found', () => {
return expect(fetchUser(999)).rejects.toThrow('Not found');
});
Testing React Components
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';
it('calls onClick when clicked', async () => {
const user = userEvent.setup();
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
await user.click(screen.getByRole('button', { name: 'Click me' }));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('shows loading state', () => {
render(<Button loading>Submit</Button>);
expect(screen.getByRole('button')).toBeDisabled();
expect(screen.getByText('Loading...')).toBeInTheDocument();
});
Coverage
jest --coverage
coverageThreshold: {
global: { statements: 80, branches: 70, functions: 80, lines: 80 },
'./src/critical/**/*.ts': { lines: 95 },
}
Common Patterns
it.each([
[1, 1, 2],
[2, 3, 5],
[0, -1, -1],
])('add(%i, %i) = %i', (a, b, expected) => {
expect(add(a, b)).toBe(expected);
});
it.skip('pending test', () => { });
it.only('focused test', () => { });
describe.only('focused suite', () => { });
expect.extend({
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling;
return { pass, message: () => `expected ${received} to be within [${floor}, ${ceiling}]` };
},
});
expect(100).toBeWithinRange(90, 110);
Debugging Failing Tests
jest --verbose --no-coverage
jest src/components/Button.test.tsx --verbose
node --inspect-brk node_modules/.bin/jest --runInBand
console.log(mockFn.mock.calls); // all calls
console.log(mockFn.mock.results); // all return values
Related Skills
- [[test-driven-development]] — TDD methodology and workflow
- [[playwright-best-practices]] — End-to-end browser testing
- [[vitest]] — Faster alternative for Vite-based projects
- [[property-based-testing]] — Generative testing with property-based approaches