Jest Testing Skill
Core Patterns
Basic Test
describe('Calculator', () => {
let calc;
beforeEach(() => { calc = new Calculator(); });
test('adds two numbers', () => {
expect(calc.add(2, 3)).toBe(5);
});
test('throws on division by zero', () => {
expect(() => calc.divide(10, 0)).toThrow('Division by zero');
});
});
Matchers
expect(value).toBe(exact); // === strict
expect(value).toEqual(object); // deep equality
expect(value).toBeTruthy();
expect(value).toBeNull();
expect(value).toBeGreaterThan(3);
expect(value).toBeCloseTo(0.3, 5);
expect(str).toMatch(/regex/);
expect(arr).toContain(item);
expect(arr).toHaveLength(3);
expect(obj).toHaveProperty('name');
expect(obj).toMatchObject({ name: 'Alice' });
expect(() => fn()).toThrow(CustomError);
Mocking
// Mock function
const mockFn = jest.fn();
mockFn.mockReturnValue(42);
mockFn.mockResolvedValue({ data: 'test' });
expect(mockFn).toHaveBeenCalledWith('arg1');
expect(mockFn).toHaveBeenCalledTimes(1);
// Mock module
jest.mock('./database');
const db = require('./database');
db.getUser.mockResolvedValue({ name: 'Alice' });
// Mock with implementation
jest.mock('./api', () => ({
fetchUsers: jest.fn().mockResolvedValue([{ name: 'Alice' }]),
}));
// Spy
const spy = jest.spyOn(console, 'log').mockImplementation();
expect(spy).toHaveBeenCalledWith('expected');
spy.mockRestore();
// Fake timers
jest.useFakeTimers();
jest.advanceTimersByTime(1000);
jest.useRealTimers();
Async Testing
test('fetches users', async () => {
const users = await fetchUsers();
expect(users).toHaveLength(3);
});
test('resolves with data', () => {
return expect(fetchData()).resolves.toEqual({ data: 'value' });
});
test('rejects with error', () => {
return expect(fetchBadData()).rejects.toThrow('not found');
});
React Component Testing (Testing Library)
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import LoginForm from './LoginForm';
test('submits login form', async () => {
const
render(<LoginForm />);
fireEvent.change(screen.getByLabelText('Email'), {
target: { value: 'user@test.com' },
});
fireEvent.change(screen.getByLabelText('Password'), {
target: { value: 'password123' },
});
fireEvent.click(screen.getByRole('button', { name: /login/i }));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith({
email: 'user@test.com', password: 'password123',
});
});
});
Snapshot Testing
test('renders correctly', () => {
const tree = renderer.create(<Button label="Click" />).toJSON();
expect(tree).toMatchSnapshot();
});
// Update: jest --updateSnapshot
Anti-Patterns
| Bad |
Good |
Why |
expect(x === y).toBe(true) |
expect(x).toBe(y) |
Better errors |
No await on async |
Always await |
Swallows failures |
| Snapshot everything |
Snapshot UI, assert logic |
Snapshot fatigue |
Quick Reference
| Task |
Command |
| Run all |
npx jest |
| Watch |
npx jest --watch |
| Coverage |
npx jest --coverage |
| Update snapshots |
npx jest --updateSnapshot |
| Run file |
npx jest tests/calc.test.js |
| Single test |
test.only('name', () => {}) |
Deep Patterns
For production-grade patterns, see reference/playbook.md:
| Section |
What's Inside |
| §1 Production Config |
Node + React configs, path aliases, coverage thresholds |
| §2 Mocking Deep Dive |
Module/partial/manual mocks, spies, timers, env vars |
| §3 Async Patterns |
Promises, rejections, event emitters, streams |
| §4 test.each |
Array, tagged template, describe.each for table-driven tests |
| §5 Custom Matchers |
toBeWithinRange, toBeValidEmail, TypeScript declarations |
| §6 React Testing Library |
userEvent, hooks, context providers |
| §7 Snapshot Testing |
Component, inline, property matchers |
| §8 API Service Testing |
Mocked axios, CRUD patterns, error handling |
| §9 Global Setup |
Multi-project config, DB setup/teardown |
| §10 CI/CD |
GitHub Actions with coverage gates |
| §11 Debugging Table |
10 common problems with fixes |
| §12 Best Practices |
15-item production checklist |
1---2name: lambdatest-jest-skill3description: Generates Jest unit and integration tests in JavaScript or TypeScript. Covers mocking, snapshots, async testing, and React component testing. Use when user mentions "Jest", "describe/it/expect", "jest.mock", "toMatchSnapshot". Triggers on: "Jest", "expect().toBe()", "jest.mock", "snapshot test", "JS test", "React test".4license: MIT5---67# Jest Testing Skill89## Core Patterns1011### Basic Test1213```javascript14describe('Calculator', () => {15 let calc;16 beforeEach(() => { calc = new Calculator(); });1718 test('adds two numbers', () => {19 expect(calc.add(2, 3)).toBe(5);20 });2122 test('throws on division by zero', () => {23 expect(() => calc.divide(10, 0)).toThrow('Division by zero');24 });25});26```2728### Matchers2930```javascript31expect(value).toBe(exact); // === strict32expect(value).toEqual(object); // deep equality33expect(value).toBeTruthy();34expect(value).toBeNull();35expect(value).toBeGreaterThan(3);36expect(value).toBeCloseTo(0.3, 5);37expect(str).toMatch(/regex/);38expect(arr).toContain(item);39expect(arr).toHaveLength(3);40expect(obj).toHaveProperty('name');41expect(obj).toMatchObject({ name: 'Alice' });42expect(() => fn()).toThrow(CustomError);43```4445### Mocking4647```javascript48// Mock function49const mockFn = jest.fn();50mockFn.mockReturnValue(42);51mockFn.mockResolvedValue({ data: 'test' });52expect(mockFn).toHaveBeenCalledWith('arg1');53expect(mockFn).toHaveBeenCalledTimes(1);5455// Mock module56jest.mock('./database');57const db = require('./database');58db.getUser.mockResolvedValue({ name: 'Alice' });5960// Mock with implementation61jest.mock('./api', () => ({62 fetchUsers: jest.fn().mockResolvedValue([{ name: 'Alice' }]),63}));6465// Spy66const spy = jest.spyOn(console, 'log').mockImplementation();67expect(spy).toHaveBeenCalledWith('expected');68spy.mockRestore();6970// Fake timers71jest.useFakeTimers();72jest.advanceTimersByTime(1000);73jest.useRealTimers();74```7576### Async Testing7778```javascript79test('fetches users', async () => {80 const users = await fetchUsers();81 expect(users).toHaveLength(3);82});8384test('resolves with data', () => {85 return expect(fetchData()).resolves.toEqual({ data: 'value' });86});8788test('rejects with error', () => {89 return expect(fetchBadData()).rejects.toThrow('not found');90});91```9293### React Component Testing (Testing Library)9495```javascript96import { render, screen, fireEvent, waitFor } from '@testing-library/react';97import '@testing-library/jest-dom';98import LoginForm from './LoginForm';99100test('submits login form', async () => {101 const onSubmit = jest.fn();102 render(<LoginForm onSubmit={onSubmit} />);103104 fireEvent.change(screen.getByLabelText('Email'), {105 target: { value: 'user@test.com' },106 });107 fireEvent.change(screen.getByLabelText('Password'), {108 target: { value: 'password123' },109 });110 fireEvent.click(screen.getByRole('button', { name: /login/i }));111112 await waitFor(() => {113 expect(onSubmit).toHaveBeenCalledWith({114 email: 'user@test.com', password: 'password123',115 });116 });117});118```119120### Snapshot Testing121122```javascript123test('renders correctly', () => {124 const tree = renderer.create(<Button label="Click" />).toJSON();125 expect(tree).toMatchSnapshot();126});127// Update: jest --updateSnapshot128```129130### Anti-Patterns131132| Bad | Good | Why |133|-----|------|-----|134| `expect(x === y).toBe(true)` | `expect(x).toBe(y)` | Better errors |135| No `await` on async | Always `await` | Swallows failures |136| Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue |137138## Quick Reference139140| Task | Command |141|------|---------|142| Run all | `npx jest` |143| Watch | `npx jest --watch` |144| Coverage | `npx jest --coverage` |145| Update snapshots | `npx jest --updateSnapshot` |146| Run file | `npx jest tests/calc.test.js` |147| Single test | `test.only('name', () => {})` |148149## Deep Patterns150151For production-grade patterns, see `reference/playbook.md`:152153| Section | What's Inside |154|---------|--------------|155| §1 Production Config | Node + React configs, path aliases, coverage thresholds |156| §2 Mocking Deep Dive | Module/partial/manual mocks, spies, timers, env vars |157| §3 Async Patterns | Promises, rejections, event emitters, streams |158| §4 test.each | Array, tagged template, describe.each for table-driven tests |159| §5 Custom Matchers | toBeWithinRange, toBeValidEmail, TypeScript declarations |160| §6 React Testing Library | userEvent, hooks, context providers |161| §7 Snapshot Testing | Component, inline, property matchers |162| §8 API Service Testing | Mocked axios, CRUD patterns, error handling |163| §9 Global Setup | Multi-project config, DB setup/teardown |164| §10 CI/CD | GitHub Actions with coverage gates |165| §11 Debugging Table | 10 common problems with fixes |166| §12 Best Practices | 15-item production checklist |