cheat sheet
Jest
Day-to-day Jest CLI commands and config patterns — running tests, mocks, snapshots, coverage, watch mode, and migration tips.
Jest — CLI and configuration
What it is
Jest is the long-incumbent JavaScript test runner: zero-config (mostly), built-in assertions, mocking, snapshots, and coverage. It still powers most legacy React, Next.js (pre-15), and large monorepos. For new Vite-based work, vitest is the modern alternative — same API, faster, ESM-native. This article focuses on day-to-day CLI use and config patterns assuming you already have Jest in the project.
Install
npm install -D jest @types/jest
# TypeScript projects — pick a transformer
npm install -D @swc/jest # SWC (10-20× faster, no type check)
# OR
npm install -D ts-jest # ts-jest (slow, includes type check)
# DOM testing (since Jest v28)
npm install -D jest-environment-jsdom
Output: (none — exits 0)
Add scripts:
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "jest --ci --coverage --reporters=default --reporters=jest-junit",
"coverage": "jest --coverage"
}
}
Day-to-day commands
| Command | What it does |
|---|---|
jest | Run every test file. Defaults to __tests__/** and *.test.{js,ts}. |
jest path/to/file.test.ts | Run one specific file. |
jest --testNamePattern="adds two" (-t) | Run tests whose name matches a regex. |
jest --watch | Re-run tests on file change. Uses git to find changed files. |
jest --watchAll | Watch every test file (no git heuristic). |
jest --coverage | Generate coverage report. Defaults to coverage/. |
jest --ci | CI-friendly mode: never write snapshots automatically, exit non-zero on missing snapshots. |
jest -u (--updateSnapshot) | Update snapshot files in place. |
jest --findRelatedTests src/foo.ts | Run tests that import the given source files. |
jest --shard=N/M | Partition test files deterministically — for parallel CI jobs. |
jest --detectOpenHandles | Find leaked timers / sockets after the suite ends. |
jest --listTests | Print resolved test files; don't run anything. |
Common scenarios
Run a single test by name
jest -t "uppercases the user name"
Output:
PASS src/user.test.ts
✓ uppercases the user name (3 ms)
Tests: 1 passed, 1 total
-t is a regex against describe + it names. Quote it if it has spaces.
Watch mode with name filter
jest --watch
# Press `p` to filter by filename, `t` to filter by test name
Output:
Watch Usage
› Press a to run all tests.
› Press f to run only failed tests.
› Press p to filter by a filename regex pattern.
› Press t to filter by a test name regex pattern.
› Press q to quit watch mode.
The watch UI is the best in the runner space — interactive prompts beat re-typing flags.
Coverage with thresholds
// jest.config.js
export default {
collectCoverage: true,
coverageDirectory: "coverage",
coverageReporters: ["text", "lcov", "json-summary"],
collectCoverageFrom: [
"src/**/*.{ts,tsx}",
"!src/**/*.d.ts",
"!src/**/index.ts",
],
coverageThreshold: {
global: { branches: 75, functions: 80, lines: 80, statements: 80 },
},
};
jest --coverage
Output:
Tests: 42 passed, 42 total
-------|---------|----------|---------|---------|
File | % Stmts | % Branch | % Funcs | % Lines |
-------|---------|----------|---------|---------|
All | 84.21 | 77.13 | 87.50 | 84.21 |
-------|---------|----------|---------|---------|
Exits non-zero if any metric falls below the floor — wire as a required PR check.
Snapshot testing
test("renders banner HTML", () => {
const html = renderToString(<Banner title="Hi" />);
expect(html).toMatchSnapshot();
});
First run writes __snapshots__/banner.test.ts.snap. After intentional UI changes:
jest -u # update all snapshots
jest -u path/to/banner.test.ts # update for one file
Output:
PASS src/banner.test.ts
› 1 snapshot updated.
Snapshots: 1 updated, 1 total
Tests: 1 passed, 1 total
For tiny outputs, prefer inline snapshots:
expect(formatPrice(99.5)).toMatchInlineSnapshot(`"$99.50"`);
Mocking modules
jest.mock is hoisted above imports — factory closures can't reference uninitialised local variables:
// src/user.test.ts
import { jest } from "@jest/globals";
import { fetchUser } from "./api.js";
import { getUserName } from "./user.js";
jest.mock("./api.js");
const mockedFetch = jest.mocked(fetchUser);
beforeEach(() => mockedFetch.mockReset());
test("uppercases name", async () => {
mockedFetch.mockResolvedValue({ name: "alice" });
expect(await getUserName(1)).toBe("ALICE");
});
jest.mocked() gives you a typed mock wrapper — much better than casting as jest.Mock.
Sharding across CI runners
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- run: jest --shard=${{ matrix.shard }}/4 --ci
Each runner takes 25% of files; total wall time roughly quartered.
React Testing Library setup
// jest.config.js
export default {
testEnvironment: "jsdom",
setupFilesAfterEach: ["<rootDir>/jest.setup.js"],
};
// jest.setup.js
import "@testing-library/jest-dom";
// Button.test.tsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Button } from "./Button";
test("calls onClick when clicked", async () => {
const onClick = jest.fn();
render(<Button onClick={onClick}>Click me</Button>);
await userEvent.click(screen.getByRole("button"));
expect(onClick).toHaveBeenCalledTimes(1);
});
Useful flags
| Flag | Purpose |
|---|---|
--bail | Stop after the first failure. Cuts CI time when most tests pass. |
--maxWorkers=2 | Cap parallel workers — useful on memory-limited CI runners. |
--maxWorkers=50% | Half of available cores. |
--runInBand | Disable parallelism. Sequential. Useful for debugging order-dependent tests. |
--silent | Suppress console output from tests. |
--verbose | Print every test name (good for debugging which test hung). |
--clearCache | Wipe Jest's transform cache. Try if you get spurious errors after dep upgrades. |
--config jest.config.ci.js | Use an alternate config — common for CI-specific overrides. |
--reporters=default --reporters=jest-junit | Multiple reporters (e.g. JUnit XML for CI dashboards). |
--changedSince=main | Only tests for files changed vs the given ref. |
Configuration
Jest loads config from (in order):
jestkey inpackage.jsonjest.config.js/.ts/.cjs/.mjsjest.config.json
Modern TS + React config
// jest.config.ts
import type { Config } from "jest";
const config: Config = {
testEnvironment: "jsdom",
setupFilesAfterEach: ["<rootDir>/jest.setup.ts"],
transform: {
"^.+\\.(t|j)sx?$": ["@swc/jest", {
jsc: { transform: { react: { runtime: "automatic" } } },
}],
},
moduleNameMapper: {
"\\.(css|less|scss)$": "identity-obj-proxy",
"^@/(.*)$": "<rootDir>/src/$1",
},
testMatch: ["**/__tests__/**/*.test.{ts,tsx}", "**/?(*.)+(spec|test).{ts,tsx}"],
collectCoverageFrom: ["src/**/*.{ts,tsx}", "!**/*.d.ts"],
};
export default config;
Multi-project (monorepo)
// jest.config.js (root)
export default {
projects: [
"<rootDir>/packages/api",
"<rootDir>/packages/web",
"<rootDir>/packages/shared",
],
};
Each sub-project has its own jest.config.js. The root aggregates results — coverage and reports merge.
Per-file environment override
/**
* @jest-environment jsdom
*/
import { render } from "@testing-library/react";
// ...
Useful when most tests are Node but a handful need a DOM.
Globals via setupFiles vs setupFilesAfterEach
| Hook | When |
|---|---|
setupFiles | Before Jest globals are available. Use for polyfills only. |
setupFilesAfterEach | After expect, jest, etc. are wired. Use for matcher extensions, global mocks. |
Common pitfalls
jest-environment-jsdomnot installed (v28+) —Cannot find module 'jest-environment-jsdom'. Install it explicitly.- ESM source with CJS Jest —
SyntaxError: Cannot use import statement outside a module. Use@swc/jestorbabel-jest, or enable experimental ESM withNODE_OPTIONS=--experimental-vm-modules. jest.mockreference error — referencing local variables in ajest.mockfactory. Usejest.doMockafter imports, or inline the value.- Tests pass alone, fail together — global state leaking. Set
resetModules: trueandclearMocks: truein config. Jest did not exit one second after the test run— open handle (timer, server, DB pool). Run with--detectOpenHandlesto find it.- Watch mode does nothing — non-git directory or no committed git history. Use
--watchAllinstead. @swc/jestskips type checking — runtsc --noEmitseparately, ideally as a parallel CI step.- Coverage is 0% for some files — they're not imported by any test, OR
collectCoverageFromexcludes them, OR they're intestPathIgnorePatterns.
See also
- Packages: npm-jest — full package reference, ecosystem, alternatives
- JavaScript: vitest — the modern Vite-native alternative
- JavaScript: playwright — E2E layer above unit tests