mrkeyoor.com_
Wed 05 Aug 05:04 UTC
npmTestingupdated 05 Aug 2026

jest

Jest is an all-in-one JavaScript testing framework: test runner, assertion library, mocking system, coverage reporting, and snapshot testing in a single install. It runs test files in parallel worker processes with isolated module registries, so tests do not leak state into each other, and its watch mode re-runs only the tests related to files you just changed. It works out of the box for CommonJS Node projects and reaches everything else through Babel transforms.

Verdict

Still the safest default for React codebases and plain Node projects, with the most battle-tested mocking and snapshot story in JavaScript. If you are starting a Vite or ESM-first project today, Vitest is the more natural fit and Jest is the one you configure around.

API stability4/5The describe/test/expect and mocking APIs have barely changed in years; majors like Jest 30 mostly drop old Node versions and tighten defaults rather than rework the API.
Docs5/5jestjs.io covers getting started, every matcher, CLI flags, and configuration with examples, plus dedicated guides for webpack, TypeScript, and timer mocking.
Maintenance4/5Actively developed under the OpenJS Foundation after leaving Meta, repo pushed August 2026 with 30.x shipping steadily, though the core team is small relative to the install base.
Ecosystem5/546M weekly downloads, first-class presets from React Native and countless frameworks, and years of Stack Overflow answers; almost every JS tool documents a Jest integration.

Use it if

  • You test React or React Native: both ecosystems treat Jest as the default and ship presets for it
  • You want mocking built in: jest.mock for modules, jest.fn for functions, and fake timers without adding sinon or proxyquire
  • You rely on snapshot testing for component output or serialized objects, which Jest popularized and still does best
  • You have a plain CommonJS Node project and want tests running with literally zero configuration
Skip it if

Setup reality

npm install --save-dev jest and a test script is genuinely all a CommonJS project needs. The annoying parts arrive with modern syntax: ESM, JSX, or TypeScript each mean installing babel-jest plus @babel/preset-env (and preset-react or preset-typescript) and a babel.config.js, or going the ts-jest route instead. DOM testing needs the separate jest-environment-jsdom package plus testEnvironment: 'jsdom' in config, because it no longer ships in the box. Jest 30 needs Node ^18.14, ^20, ^22, or >=24 and TypeScript 5.4+ if you use its types. Expect to spend more time on the transform config than on your first hundred tests.

Patterns

Write and run a first testbasic-test

// sum.js
function sum(a, b) {
  return a + b;
}
module.exports = sum;

// sum.test.js
const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

Files matching *.test.js or living in __tests__/ are picked up automatically; no config file is required for CommonJS.

Test async codeasync-test

test('fetches user', async () => {
  const user = await getUser(1);
  expect(user.name).toBe('Ada');
});

test('rejects on missing user', async () => {
  await expect(getUser(999)).rejects.toThrow('not found');
});

Forgetting the await before expect(...).rejects makes the test pass before the promise settles.

Mock a callback with jest.fnmock-function

test('calls the handler once per item', () => {
  const handler = jest.fn().mockReturnValue(true);
  [1, 2].forEach(handler);

  expect(handler).toHaveBeenCalledTimes(2);
  expect(handler).toHaveBeenCalledWith(1, 0, [1, 2]);
});

forEach passes (value, index, array), so toHaveBeenCalledWith needs all three arguments or it fails confusingly.

Mock an entire modulemock-module

jest.mock('./api');
const api = require('./api');
const { loadDashboard } = require('./dashboard');

test('renders fetched data', async () => {
  api.fetchStats.mockResolvedValue({ users: 5 });
  const result = await loadDashboard();
  expect(result.users).toBe(5);
});

jest.mock calls are hoisted above imports automatically for CJS; under experimental ESM you must use jest.unstable_mockModule instead.

Spy on a method and restore itspy-on-method

afterEach(() => {
  jest.restoreAllMocks();
});

test('warns on bad input', () => {
  const warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
  parse('not-json');
  expect(warn).toHaveBeenCalled();
});

Without restoreAllMocks (or restoreMocks: true in config) the spy leaks into every later test in the file.

Control setTimeout with fake timersfake-timers

jest.useFakeTimers();

test('debounces calls', () => {
  const fn = jest.fn();
  const debounced = debounce(fn, 1000);
  debounced();
  debounced();

  jest.advanceTimersByTime(1000);
  expect(fn).toHaveBeenCalledTimes(1);
});

Modern fake timers also fake Date and queueMicrotask; use jest.useRealTimers() in afterEach if other tests need real time.

Snapshot a serializable valuesnapshot-test

test('config shape stays stable', () => {
  expect(buildConfig('prod')).toMatchSnapshot();
});

test('small values inline', () => {
  expect(slugify('Hello World')).toMatchInlineSnapshot(`"hello-world"`);
});

Update stale snapshots with jest -u, but review the diff first; blindly updating turns snapshots into noise.

Share setup between testssetup-teardown

let db;

beforeAll(async () => {
  db = await connect();
});

beforeEach(() => db.reset());

afterAll(async () => {
  await db.close();
});

beforeAll runs once per file, not once per suite run; each parallel worker gets its own copy of module state.

Run one test over many casesparametrized-test

test.each([
  [1, 1, 2],
  [2, 2, 4],
  [0, 5, 5],
])('sum(%i, %i) === %i', (a, b, expected) => {
  expect(sum(a, b)).toBe(expected);
});

The template string variant test.each`...` reads better for named fields but breaks some editor test runners' go-to-test.

Run only what you are working onrun-single-test

# one file
npx jest sum.test.js
# tests whose name matches
npx jest -t "adds 1"
# re-run on change, only affected tests
npx jest --watch

A committed test.only silently skips the rest of the file; ESLint's jest plugin has a rule to catch it.

Test DOM code with jsdomjsdom-environment

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
};

// install first:
// npm i -D jest-environment-jsdom

jsdom is not bundled with Jest anymore; without the separate package this config errors at startup.

Collect coverage and enforce a floorcoverage-report

// jest.config.js
module.exports = {
  collectCoverage: true,
  collectCoverageFrom: ['src/**/*.js'],
  coverageThreshold: {
    global: { branches: 80, lines: 90 },
  },
};

Without collectCoverageFrom, files no test ever imports are invisible to the report, inflating your numbers.

Alternatives

PackageRegistryPick it when
vitestnpmYou use Vite or want native ESM and TypeScript with a Jest-compatible API and faster watch mode.
mochanpmYou want a minimal runner and prefer picking your own assertion and mocking libraries.
avanpmYou want concurrent tests in isolated processes with a small, opinionated API and no globals.