When to Use
Use this skill when you are:
- Adding or refactoring Jest tests.
- Designing a mocking strategy (unit vs integration boundaries).
- Testing async code (Promises, async/await, callbacks).
- Testing time-dependent logic (timers, debouncing, polling).
- Introducing snapshots or maintaining existing snapshots.
- Standardizing setup/teardown and test isolation.
Critical Patterns
1) Determinism and isolation (MUST)
- MUST keep tests deterministic: no real network, no real time, no shared global mutable state.
- MUST isolate side effects:
- Prefer dependency injection.
- Reset/clear mocks between tests.
- Use setup/teardown hooks (
beforeEach, afterEach, etc.). (archive.jestjs.io)
2) Arrange / Act / Assert (MUST)
3) Matchers: be precise (MUST)
- MUST choose the most precise matcher:
toBe for exact equality (uses Object.is).
toEqual for deep equality (objects/arrays).
toBeCloseTo for floats.
toThrow requires wrapping the call in a function. (archive.jestjs.io)
4) Async tests: don’t leak promises (MUST)
MUST test async code using one of these correct patterns:
- Return the Promise
- Use
async/await
- Use
done callback only for legacy callback APIs
MUST assert errors explicitly:
await expect(promise).rejects.toThrow(...)
- or
return expect(promise).rejects...
MUST prevent false positives:
- Use
expect.assertions(n) or expect.hasAssertions() when appropriate. (archive.jestjs.io)
5) Setup/teardown: keep state clean (MUST)
MUST use lifecycle hooks:
beforeEach/afterEach for per-test setup/cleanup.
beforeAll/afterAll for expensive one-time setup.
MUST keep cleanup symmetrical (what you create, you destroy). (archive.jestjs.io)
6) Mocking strategy: mock boundaries, not implementation details (MUST)
MUST prefer mocking system boundaries:
- HTTP, DB, filesystem, timers, external SDKs.
SHOULD avoid mocking internal helpers if behavior can be tested through public API.
MUST prefer jest.spyOn when you want to observe/override a real method.
MUST understand module mocks:
jest.mock() replaces a module.
- manual mocks live in
__mocks__/.
- use
jest.requireActual() when you need partial mocking. (archive.jestjs.io)
7) Mock functions: verify behavior, not noise (MUST)
MUST assert the important interactions:
toHaveBeenCalledTimes, toHaveBeenCalledWith, toHaveBeenNthCalledWith.
MUST keep call assertions focused (avoid over-specifying order unless it matters).
MUST reset mock state between tests (clearAllMocks/resetAllMocks/restoreAllMocks) according to repo policy. (archive.jestjs.io)
8) Timers: use fake timers for time-based logic (MUST)
- MUST use
jest.useFakeTimers() for debounce/throttle/polling logic.
- MUST advance time explicitly (
advanceTimersByTime, runAllTimers).
- MUST restore real timers after the test to avoid cross-test contamination. (archive.jestjs.io)
9) Snapshots: only for stable output (SHOULD)
- SHOULD use snapshots for stable, meaningful serialized output.
- SHOULD NOT snapshot large or frequently-changing structures.
- MUST keep snapshots small; prefer targeted assertions when possible.
- Use inline/property matchers to reduce brittleness. (archive.jestjs.io)
10) Platform/environment: choose correctly (MUST)
Code Examples
Matchers (precision)
test('deep equality for objects', () => {
expect({ a: 1 }).toEqual({ a: 1 });
expect({ a: 1 }).not.toBe({ a: 1 }); // different references
});
test('float comparisons', () => {
expect(0.1 + 0.2).toBeCloseTo(0.3);
});
test('throws requires wrapping function', () => {
const fn = () => {
throw new Error('boom');
};
expect(fn).toThrow(/boom/);
});
Async (preferred patterns)
test('async/await', async () => {
const result = await Promise.resolve(42);
expect(result).toBe(42);
});
test('rejects', async () => {
await expect(Promise.reject(new Error('boom'))).rejects.toThrow('boom');
});
test('assertions count (prevents false positives)', async () => {
expect.assertions(1);
try {
await Promise.reject(new Error('boom'));
} catch {
expect(true).toBe(true);
}
});
Setup/teardown + cleanup
afterEach(() => {
jest.clearAllMocks();
});
Spies and module mocks (safe partial mocking)
import * as http from './http';
test('spyOn a real method', async () => {
jest.spyOn(http, 'get').mockResolvedValue({ ok: true } as any);
await loadData();
expect(http.get).toHaveBeenCalledTimes(1);
});
jest.mock('./config', () => {
const actual = jest.requireActual('./config');
return {
...actual,
FEATURE_FLAG: true,
};
});
Timers (debounce)
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
test('debounces', () => {
const fn = jest.fn();
const debounced = debounce(fn, 200);
debounced();
debounced();
jest.advanceTimersByTime(200);
expect(fn).toHaveBeenCalledTimes(1);
});
Snapshots (small and intentional)
test('serializes stable output', () => {
const output = { version: 1, items: ['a', 'b'] };
expect(output).toMatchSnapshot();
});
Commands
# Run all tests
npm test
# Run a single file
npx jest path/to/file.test.ts
# Watch mode
npx jest --watch
# Update snapshots
npx jest -u
# Coverage
npx jest --coverage
# CI-friendly (reduces flakiness in some environments)
npx jest --runInBand
Resources
1---2name: jest3description: Jest testing patterns and best practices for modern TypeScript/JavaScript projects. Trigger: Writing, refactoring, or reviewing Jest unit/integration tests (matchers, async, mocks, timers, snapshots, setup/teardown).4---56## When to Use78Use this skill when you are:910- Adding or refactoring Jest tests.11- Designing a mocking strategy (unit vs integration boundaries).12- Testing async code (Promises, async/await, callbacks).13- Testing time-dependent logic (timers, debouncing, polling).14- Introducing snapshots or maintaining existing snapshots.15- Standardizing setup/teardown and test isolation.1617---1819## Critical Patterns2021### 1) Determinism and isolation (MUST)2223- **MUST** keep tests deterministic: no real network, no real time, no shared global mutable state.24- **MUST** isolate side effects:25 - Prefer dependency injection.26 - Reset/clear mocks between tests.27 - Use setup/teardown hooks (`beforeEach`, `afterEach`, etc.). ([archive.jestjs.io](https://archive.jestjs.io/docs/en/setup-teardown))2829### 2) Arrange / Act / Assert (MUST)3031- **MUST** structure each test as:32 - Arrange (inputs + dependencies)33 - Act (execute)34 - Assert (observable outcome)3536- Avoid “mega tests” that validate many behaviors at once.3738### 3) Matchers: be precise (MUST)3940- **MUST** choose the most precise matcher:41 - `toBe` for exact equality (uses `Object.is`).42 - `toEqual` for deep equality (objects/arrays).43 - `toBeCloseTo` for floats.44 - `toThrow` requires wrapping the call in a function. ([archive.jestjs.io](https://archive.jestjs.io/docs/en/using-matchers))4546### 4) Async tests: don’t leak promises (MUST)4748- **MUST** test async code using **one** of these correct patterns:49 1. Return the Promise50 2. Use `async/await`51 3. Use `done` callback **only** for legacy callback APIs5253- **MUST** assert errors explicitly:54 - `await expect(promise).rejects.toThrow(...)`55 - or `return expect(promise).rejects...`5657- **MUST** prevent false positives:58 - Use `expect.assertions(n)` or `expect.hasAssertions()` when appropriate. ([archive.jestjs.io](https://archive.jestjs.io/docs/en/asynchronous))5960### 5) Setup/teardown: keep state clean (MUST)6162- **MUST** use lifecycle hooks:63 - `beforeEach/afterEach` for per-test setup/cleanup.64 - `beforeAll/afterAll` for expensive one-time setup.6566- **MUST** keep cleanup symmetrical (what you create, you destroy). ([archive.jestjs.io](https://archive.jestjs.io/docs/en/setup-teardown))6768### 6) Mocking strategy: mock boundaries, not implementation details (MUST)6970- **MUST** prefer mocking system boundaries:71 - HTTP, DB, filesystem, timers, external SDKs.7273- **SHOULD** avoid mocking internal helpers if behavior can be tested through public API.74- **MUST** prefer `jest.spyOn` when you want to observe/override a real method.75- **MUST** understand module mocks:76 - `jest.mock()` replaces a module.77 - manual mocks live in `__mocks__/`.78 - use `jest.requireActual()` when you need partial mocking. ([archive.jestjs.io](https://archive.jestjs.io/docs/en/mock-functions))7980### 7) Mock functions: verify behavior, not noise (MUST)8182- **MUST** assert the important interactions:83 - `toHaveBeenCalledTimes`, `toHaveBeenCalledWith`, `toHaveBeenNthCalledWith`.8485- **MUST** keep call assertions focused (avoid over-specifying order unless it matters).86- **MUST** reset mock state between tests (`clearAllMocks`/`resetAllMocks`/`restoreAllMocks`) according to repo policy. ([archive.jestjs.io](https://archive.jestjs.io/docs/en/mock-functions))8788### 8) Timers: use fake timers for time-based logic (MUST)8990- **MUST** use `jest.useFakeTimers()` for debounce/throttle/polling logic.91- **MUST** advance time explicitly (`advanceTimersByTime`, `runAllTimers`).92- **MUST** restore real timers after the test to avoid cross-test contamination. ([archive.jestjs.io](https://archive.jestjs.io/docs/en/timer-mocks))9394### 9) Snapshots: only for stable output (SHOULD)9596- **SHOULD** use snapshots for stable, meaningful serialized output.97- **SHOULD NOT** snapshot large or frequently-changing structures.98- **MUST** keep snapshots small; prefer targeted assertions when possible.99- Use inline/property matchers to reduce brittleness. ([archive.jestjs.io](https://archive.jestjs.io/docs/en/snapshot-testing))100101### 10) Platform/environment: choose correctly (MUST)102103- **MUST** select the correct `testEnvironment`:104 - `node` for backend/library code.105 - `jsdom` for DOM-dependent code.106107- **MUST** avoid accidental DOM dependencies in node tests. ([archive.jestjs.io](https://archive.jestjs.io/docs/en/jest-platform))108109---110111## Code Examples112113### Matchers (precision)114115```ts116test('deep equality for objects', () => {117 expect({ a: 1 }).toEqual({ a: 1 });118 expect({ a: 1 }).not.toBe({ a: 1 }); // different references119});120121test('float comparisons', () => {122 expect(0.1 + 0.2).toBeCloseTo(0.3);123});124125test('throws requires wrapping function', () => {126 const fn = () => {127 throw new Error('boom');128 };129 expect(fn).toThrow(/boom/);130});131```132133### Async (preferred patterns)134135```ts136test('async/await', async () => {137 const result = await Promise.resolve(42);138 expect(result).toBe(42);139});140141test('rejects', async () => {142 await expect(Promise.reject(new Error('boom'))).rejects.toThrow('boom');143});144145test('assertions count (prevents false positives)', async () => {146 expect.assertions(1);147 try {148 await Promise.reject(new Error('boom'));149 } catch {150 expect(true).toBe(true);151 }152});153```154155### Setup/teardown + cleanup156157```ts158afterEach(() => {159 jest.clearAllMocks();160});161```162163### Spies and module mocks (safe partial mocking)164165```ts166import * as http from './http';167168test('spyOn a real method', async () => {169 jest.spyOn(http, 'get').mockResolvedValue({ ok: true } as any);170171 await loadData();172173 expect(http.get).toHaveBeenCalledTimes(1);174});175176jest.mock('./config', () => {177 const actual = jest.requireActual('./config');178 return {179 ...actual,180 FEATURE_FLAG: true,181 };182});183```184185### Timers (debounce)186187```ts188beforeEach(() => {189 jest.useFakeTimers();190});191192afterEach(() => {193 jest.useRealTimers();194});195196test('debounces', () => {197 const fn = jest.fn();198 const debounced = debounce(fn, 200);199200 debounced();201 debounced();202203 jest.advanceTimersByTime(200);204205 expect(fn).toHaveBeenCalledTimes(1);206});207```208209### Snapshots (small and intentional)210211```ts212test('serializes stable output', () => {213 const output = { version: 1, items: ['a', 'b'] };214 expect(output).toMatchSnapshot();215});216```217218---219220## Commands221222```bash223# Run all tests224npm test225226# Run a single file227npx jest path/to/file.test.ts228229# Watch mode230npx jest --watch231232# Update snapshots233npx jest -u234235# Coverage236npx jest --coverage237238# CI-friendly (reduces flakiness in some environments)239npx jest --runInBand240```241242---243244## Resources245246- **Matchers / expect** (precision and choosing the right matcher). ([archive.jestjs.io](https://archive.jestjs.io/docs/en/using-matchers))247- **Async testing** (promises, async/await, callbacks, assertions count). ([archive.jestjs.io](https://archive.jestjs.io/docs/en/asynchronous))248- **Setup & teardown** (hooks and isolation). ([archive.jestjs.io](https://archive.jestjs.io/docs/en/setup-teardown))249- **Mock functions & API** (`jest.fn`, call assertions, mock APIs). ([archive.jestjs.io](https://archive.jestjs.io/docs/en/mock-functions))250- **Manual & class mocks** (`__mocks__`, module/class mocking patterns). ([archive.jestjs.io](https://archive.jestjs.io/docs/en/manual-mocks))251- **Timers** (fake timers, advancing time). ([archive.jestjs.io](https://archive.jestjs.io/docs/en/timer-mocks))252- **Snapshots** (when to use and how to keep them stable). ([archive.jestjs.io](https://archive.jestjs.io/docs/en/snapshot-testing))253- **Mocking best practices** (boundary-first mocking strategy). ([medium.com](https://medium.com/%40anjisingavaram/best-practices-for-mocking-in-unit-tests-using-jest-f8072e482864))