Jest Testing Skill
When to Use
Use this skill when you need 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",...
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 |
Limitations
- Use this skill only when the task clearly matches its upstream source and local project context.
- Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.
- Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.
1---2name: 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".4license: MIT5---67# Jest Testing Skill8## When to Use910Use this skill when you need 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",...111213## Core Patterns1415### Basic Test1617```javascript18describe('Calculator', () => {19 let calc;20 beforeEach(() => { calc = new Calculator(); });2122 test('adds two numbers', () => {23 expect(calc.add(2, 3)).toBe(5);24 });2526 test('throws on division by zero', () => {27 expect(() => calc.divide(10, 0)).toThrow('Division by zero');28 });29});30```3132### Matchers3334```javascript35expect(value).toBe(exact); // === strict36expect(value).toEqual(object); // deep equality37expect(value).toBeTruthy();38expect(value).toBeNull();39expect(value).toBeGreaterThan(3);40expect(value).toBeCloseTo(0.3, 5);41expect(str).toMatch(/regex/);42expect(arr).toContain(item);43expect(arr).toHaveLength(3);44expect(obj).toHaveProperty('name');45expect(obj).toMatchObject({ name: 'Alice' });46expect(() => fn()).toThrow(CustomError);47```4849### Mocking5051```javascript52// Mock function53const mockFn = jest.fn();54mockFn.mockReturnValue(42);55mockFn.mockResolvedValue({ data: 'test' });56expect(mockFn).toHaveBeenCalledWith('arg1');57expect(mockFn).toHaveBeenCalledTimes(1);5859// Mock module60jest.mock('./database');61const db = require('./database');62db.getUser.mockResolvedValue({ name: 'Alice' });6364// Mock with implementation65jest.mock('./api', () => ({66 fetchUsers: jest.fn().mockResolvedValue([{ name: 'Alice' }]),67}));6869// Spy70const spy = jest.spyOn(console, 'log').mockImplementation();71expect(spy).toHaveBeenCalledWith('expected');72spy.mockRestore();7374// Fake timers75jest.useFakeTimers();76jest.advanceTimersByTime(1000);77jest.useRealTimers();78```7980### Async Testing8182```javascript83test('fetches users', async () => {84 const users = await fetchUsers();85 expect(users).toHaveLength(3);86});8788test('resolves with data', () => {89 return expect(fetchData()).resolves.toEqual({ data: 'value' });90});9192test('rejects with error', () => {93 return expect(fetchBadData()).rejects.toThrow('not found');94});95```9697### React Component Testing (Testing Library)9899```javascript100import { render, screen, fireEvent, waitFor } from '@testing-library/react';101import '@testing-library/jest-dom';102import LoginForm from './LoginForm';103104test('submits login form', async () => {105 const onSubmit = jest.fn();106 render(<LoginForm onSubmit={onSubmit} />);107108 fireEvent.change(screen.getByLabelText('Email'), {109 target: { value: 'user@test.com' },110 });111 fireEvent.change(screen.getByLabelText('Password'), {112 target: { value: 'password123' },113 });114 fireEvent.click(screen.getByRole('button', { name: /login/i }));115116 await waitFor(() => {117 expect(onSubmit).toHaveBeenCalledWith({118 email: 'user@test.com', password: 'password123',119 });120 });121});122```123124### Snapshot Testing125126```javascript127test('renders correctly', () => {128 const tree = renderer.create(<Button label="Click" />).toJSON();129 expect(tree).toMatchSnapshot();130});131// Update: jest --updateSnapshot132```133134### Anti-Patterns135136| Bad | Good | Why |137|-----|------|-----|138| `expect(x === y).toBe(true)` | `expect(x).toBe(y)` | Better errors |139| No `await` on async | Always `await` | Swallows failures |140| Snapshot everything | Snapshot UI, assert logic | Snapshot fatigue |141142## Quick Reference143144| Task | Command |145|------|---------|146| Run all | `npx jest` |147| Watch | `npx jest --watch` |148| Coverage | `npx jest --coverage` |149| Update snapshots | `npx jest --updateSnapshot` |150| Run file | `npx jest tests/calc.test.js` |151| Single test | `test.only('name', () => {})` |152153## Deep Patterns154155For production-grade patterns, see `reference/playbook.md`:156157| Section | What's Inside |158|---------|--------------|159| §1 Production Config | Node + React configs, path aliases, coverage thresholds |160| §2 Mocking Deep Dive | Module/partial/manual mocks, spies, timers, env vars |161| §3 Async Patterns | Promises, rejections, event emitters, streams |162| §4 test.each | Array, tagged template, describe.each for table-driven tests |163| §5 Custom Matchers | toBeWithinRange, toBeValidEmail, TypeScript declarations |164| §6 React Testing Library | userEvent, hooks, context providers |165| §7 Snapshot Testing | Component, inline, property matchers |166| §8 API Service Testing | Mocked axios, CRUD patterns, error handling |167| §9 Global Setup | Multi-project config, DB setup/teardown |168| §10 CI/CD | GitHub Actions with coverage gates |169| §11 Debugging Table | 10 common problems with fixes |170| §12 Best Practices | 15-item production checklist |171172## Limitations173174- Use this skill only when the task clearly matches its upstream source and local project context.175- Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.176- Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.