Jest Cheatsheet - Matchers, Mocks & Async Testing

Tests only earn their keep when a failure tells you precisely what broke. This reference covers describe/it grouping, matchers beyond toBe, async and promise testing, jest.fn/jest.mock module mocking, snapshot testing, and coverage output. It emphasizes the matchers and mock strategies that pinpoint broken behavior instead of vague red. For teams adding real unit coverage to JS/TS code. After reading you write focused assertions and mock the exact seams you intend to.

Languages·44 commands·Last updated 2026-07-21

Typical Use Case

For developers writing reliable unit tests in Node.js or the browser, especially where async code, timers and module dependencies get in the way. This sheet covers four layers: organising cases with test, describe, only and skip; expressing expectations with toBe, toEqual, toHaveLength and toMatch; handling asynchrony through async callbacks, resolves and rejects, done callbacks and fake timers; and isolating dependencies with jest.fn, jest.spyOn and jest.mock so you can verify interactions with the toHaveBeenCalled family. It closes with snapshots and the everyday commands --coverage, --watch, --bail and -t. The pitfalls section concentrates on the classic incidents that turn CI red: a forgotten return that leaves async assertions un-awaited, mocks that are never restored and pollute later cases, snapshots committed by accident, and test.only left behind after debugging.

Basic Tests 6

test("desc", () => { expect(1 + 2).toBe(3) })
Define a test case
it("desc", fn)
Alias of test, more natural BDD style
describe("module", () => { it(...) })
Group related tests
test.skip("skip", () => {})
Skip this test temporarily
test.only("focus", () => {})
Run only this test (debug)
test.todo("todo")
Placeholder for a pending test

Matchers 8

expect(value).toBe(other)
Strict equality (Object.is)
expect(obj).toEqual({ a: 1 })
Deep equality (recursive)
expect(arr).toHaveLength(3)
Length of array/string
expect(str).toMatch(/regex/)
Match a string by regex
expect(arr).toContain(item)
Array contains an item
expect(value).toBeTruthy()
Truthy check (toBeFalsy opposite)
expect(fn).toThrow("error")
Expect a thrown error
expect(value).toBeNull()
Null check (also toBeUndefined/toBeDefined)

Async Tests 6

test("async", async () => { await fn() })
async/await style
test("promise", () => fn().then(d => expect(d).toBe(1)))
Return a promise for Jest to await
expect(promise).resolves.toBe(value)
Assert the resolved value
expect(promise).rejects.toThrow("err")
Assert the rejection reason
test("done", done => { fn(done) })
done callback style (manual)
jest.useFakeTimers()
Fake timers (with runAllTimers)

Hooks 4

beforeEach(() => setup())
Before each test
afterEach(() => cleanup())
After each test
beforeAll(() => init())
Once before all tests
afterAll(() => teardown())
Once after all tests

Mock Functions 8

const fn = jest.fn()
Create a mock function
jest.fn().mockReturnValue(42)
Set a fixed return value
jest.fn().mockResolvedValue(val)
Return a resolved promise
jest.fn().mockImplementation(n => n * 2)
Custom implementation
jest.spyOn(obj, "method")
Spy on a method (keeps impl)
jest.mock("./module")
Auto-mock a whole module
jest.requireActual("./module")
Require the real module in a mock
fn.mockClear() / mockReset() / mockRestore()
Clear/reset/restore a mock

Assertions & Call Verification 5

expect(fn).toHaveBeenCalled()
Verify a function was called
expect(fn).toHaveBeenCalledWith(arg)
Verify call arguments
expect(fn).toHaveBeenCalledTimes(2)
Verify call count
expect(fn).toHaveBeenLastCalledWith(arg)
Verify the last call args
expect(obj).toMatchObject({ a: 1 })
Partial object match

Snapshots & CLI 7

expect(tree).toMatchSnapshot()
Create/compare snapshot (-u to update)
expect(obj).toMatchInlineSnapshot()
Inline snapshot (written to file)
expect(value).toBeInstanceOf(Class)
Instance type assertion
npx jest --coverage
Generate a coverage report
npx jest --watch
Watch mode (rerun related; --watchAll for all)
npx jest --bail
Stop at first failure (--bail)
npx jest -t "case name"
Filter by test name regex (-t)

Parameter matrix

ParameterEffectExample
test("case name", fn)Define a test casetest("case name", () => { expect(1 + 2).toBe(3) })
it("case name", fn)Alias for test, reads more naturally in BDD styleit("case name", fn)
describe("module name", fn)Group related test cases for a clearer output hierarchydescribe("module name", () => { it(...) })
test.skipTemporarily skip this test casetest.skip("pending case", () => {})
test.onlyRun only this test case, for debuggingtest.only("focused case", () => {})
test.todoA placeholder test case to be implementedtest.todo("pending case")
expect(v).toBe(x)Strict equality (Object.is)expect(value).toBe(other)
expect(obj).toEqual({ a: 1 })Deep equality, recursive comparisonexpect(obj).toEqual({ a: 1 })
expect(arr).toHaveLength(3)Assert the length of an array or stringexpect(arr).toHaveLength(3)
expect(str).toMatch(/regex/)Match a string against a regexexpect(str).toMatch(/regex/)
expect(arr).toContain(item)Assert that an array contains an elementexpect(arr).toContain(item)
expect(v).toBeTruthy() / toBeFalsy()Truthy / falsy checkexpect(value).toBeTruthy()
expect(fn).toThrow("error")Assert that the call throws, optionally matching the message.expect(fn).toThrow("error")
expect(v).toBeNull()Check for a null value; toBeUndefined and toBeDefined cover the neighbours.expect(value).toBeNull()
expect(v).toBeInstanceOf(C)Assert on the instance type.expect(value).toBeInstanceOf(Class)
expect(obj).toMatchObject({ a: 1 })Partial object match, comparing only the fields you list.expect(obj).toMatchObject({ a: 1 })
test("async", async () => {...})The async/await form; Jest waits for the returned promise on its own.test("async", async () => { await fn() })
() => fn().then(d => expect(d).toBe(1))Return a promise and Jest waits for it to settle.test("promise", () => fn().then(d => expect(d).toBe(1)))
expect(p).resolves.toBe(v)Assert the value the promise resolves to.expect(promise).resolves.toBe(value)
expect(p).rejects.toThrow("err")Assert the reason the promise rejects.expect(promise).rejects.toThrow("err")
test("done", done => {...})The done-callback form, which you have to call yourself.test("done", done => { fn(done) })
jest.useFakeTimers()Swap the real timers for fake ones you control.jest.useFakeTimers()
jest.runAllTimers()Run every pending fake timer through to completion.jest.runAllTimers()
beforeEach / afterEachRuns once before and once after each test case.beforeEach(() => setup())
beforeAll / afterAllRuns once before and once after the whole block.beforeAll(() => init())
jest.fn()Create a mock function.const fn = jest.fn()
jest.fn().mockReturnValue(42)Give the mock a fixed return value.jest.fn().mockReturnValue(42)
jest.fn().mockResolvedValue(v)Make the mock return an already-resolved promise.jest.fn().mockResolvedValue(val)
jest.fn().mockImplementation(fn)Supply your own implementation for the mock.jest.fn().mockImplementation(n => n * 2)
jest.spyOn(obj, "method")Wrap a method for observation, keeping the real implementation behind it.jest.spyOn(obj, "method")
jest.mock("./module")Auto-mock every export of the module.jest.mock("./module")
jest.requireActual("./module")Load the genuine module from inside a mocked one.jest.requireActual("./module")
mockClear / mockReset / mockRestoreClear the call record, drop the implementation, or put the original method back.fn.mockClear() / mockReset() / mockRestore()
toHaveBeenCalled()Assert the function was called at least once.expect(fn).toHaveBeenCalled()
toHaveBeenCalledWith(arg)Assert the arguments a call received.expect(fn).toHaveBeenCalledWith(arg)
toHaveBeenCalledTimes(2)Assert how many times it was called.expect(fn).toHaveBeenCalledTimes(2)
toHaveBeenLastCalledWith(arg)Assert the arguments of the most recent call.expect(fn).toHaveBeenLastCalledWith(arg)
toMatchSnapshot()Create a snapshot on the first run, compare against it afterwards.expect(tree).toMatchSnapshot()
toMatchInlineSnapshot()Store the snapshot inline in the test file itself.expect(obj).toMatchInlineSnapshot()
npx jest --coverageProduce a coverage report.npx jest --coverage
npx jest --watchWatch for changes and re-run only the affected tests.npx jest --watch
npx jest --watchAllWatch every test file.npx jest --watchAll
npx jest --bailStop at the first failing test.npx jest --bail
npx jest -t "case name"Filter the run by a regular expression against the test name.npx jest -t "case name"

Common pitfalls

SymptomThe test always passes, even if you change the asserted value to something obviously wrong.

CauseThe async test body has no return or await, so Jest decides the test finished before the Promise settles and the assertion never runs.

FixDeclare the test function async and add await to the async call, or return the Promise directly, so Jest waits correctly.

SymptomA single test passes on its own but fails when the whole suite runs, with the failure point drifting.

CauseThere is shared state between tests: spies, mocks, or globals left over from the previous test are not cleaned up.

FixCall mockRestore() / mockClear() in afterEach to restore replaced methods, or use jest.resetModules() to clear the module cache.

SymptomA batch of new failing snapshots suddenly appears on CI while it is green locally.

CauseThe structural change from the code edit should have updated the snapshot, but the snapshot was committed with the old value and CI's comparison fails.

FixAfter confirming the change is intentional, run locally and update snapshots with -u and commit the new snapshots together; do not blindly add -u in CI.

SymptomAfter committing, many of colleagues' tests mysteriously fail and you suspect you changed shared config.

CauseA leftover test.only from debugging ran only the focused test and silently skipped the rest, which all surface once merged into the main branch.

FixBefore committing, search globally for test.only and describe.only to confirm they are removed; you can also use CI flags to ensure skip logic does not take effect.

SymptomTests for timer-related code never finish and fail only on timeout.

CauseReal timers require real time to wait and may hang the test in combination with network requests.

FixSwitch to jest.useFakeTimers() to replace the real timers, then advance synchronously with jest.runAllTimers() so the test completes instantly.

SymptomAfter mocking an entire module, the code under test gets empty implementations and reports undefined.

Causejest.mock replaces all exports of the module with jest.fn(), which returns undefined by default.

FixProvide the needed return values with mockReturnValue / mockResolvedValue; if you only want partial real behavior, mix in the real implementation with jest.requireActual("./module").

SymptomComparing two objects with identical content using toBe fails the assertion.

CausetoBe is reference equality, and an object is a new reference each time it is constructed.

FixUse toEqual for deep comparison; use toMatchObject when only some fields matter.

Troubleshooting

  1. 1Filter by test name to quickly locate a failing test

    npx jest -t "case name"

    Runs only tests whose names match the regex, avoiding wasting time on a full run.

  2. 2Stop at the first failure and look at the most critical problem first

    npx jest --bail

    Stops running immediately on any failure, and the output points to the first place that broke the chain.

  3. 3Generate a coverage report to find untested branches

    npx jest --coverage

    The output lists statement and branch coverage by file, with uncovered lines listed directly.

  4. 4Update snapshots whose changes are confirmed

    npx jest -u

    Writes the new value over the outdated snapshot; before committing you should manually review the diff to confirm the change is intentional.

  5. 5Verify that a mock was called correctly

    expect(fn).toHaveBeenCalledWith(arg)

    The failure message prints the actual call arguments so you can directly fix the implementation or the assertion against the expectation.

  6. 6Advance the fake timers in lockstep so async tests do not time out

    jest.useFakeTimers(); jest.runAllTimers()

    The two calls belong together: they drain every pending timer callback before you get to assert.

Command Examples

Async tests: return, await, or resolves

test("resolves", () => save().then((d) => expect(d).toBe(1))); test("async/await", async () => { const d = await save(); expect(d).toBe(1); }); test("resolves matcher", () => { expect(save()).resolves.toBe(1); expect(save()).rejects.toThrow("failed"); });

Without await or return, Jest finishes the test before the promise settles and the assertion never runs — a broken test shows green. async + await is the clearest habit; resolves/rejects is shorter when you check a single value.

Mocks: fixed return, async return, and call assertions

const send = jest.fn(); send.mockResolvedValue({ ok: true }); await handler(send); expect(send).toHaveBeenCalled(); expect(send).toHaveBeenCalledTimes(2); expect(send).toHaveBeenLastCalledWith({ id: 7 }); send.mockClear();

Asserting only toHaveBeenCalled() cannot distinguish one call from five or catch a wrong argument. toHaveBeenCalledTimes and toHaveBeenLastCalledWith are what actually stop the regression. Reusing one mock across tests requires mockClear, otherwise counts accumulate across cases.

Partial mocking: spyOn keeps the real implementation

const spy = jest.spyOn(api, "fetch"); spy.mockResolvedValue({ data: [] }); // when the remaining exports must stay real const real = jest.requireActual("./module"); afterEach(() => { spy.mockRestore(); });

jest.mock("./module") replaces every export with an auto-mock, so even helper functions you never intended to touch become undefined. When you only need one method replaced, use spyOn and mockRestore it in afterEach, or it leaks into every later test.

Fake timers: make debounce and retry logic finish instantly

jest.useFakeTimers(); test("debounce fires once", () => { const spy = jest.fn(); debounce(spy, 300); debounce(spy, 300); jest.runAllTimers(); expect(spy).toHaveBeenCalledTimes(1); });

Without fake timers you genuinely wait 300 ms, which is both slow and flaky — a slower machine turns it into an intermittent failure. runAllTimers drains the pending queue instantly. Restore real timers afterwards, or genuine timer-based logic in later tests hangs forever.

Fold setup and cleanup into lifecycle hooks

beforeAll(() => (server = startServer())); beforeEach(() => setup()); afterEach(() => cleanup()); afterAll(() => teardown());

Putting setup and teardown in hooks rather than copying them into every it avoids the "added a test and forgot the cleanup" leak. Missing cleanup usually does not throw; it corrupts the next case and produces phantom failures that only appear when a test runs alone.

Common Pitfalls

  • Forgetting await or return lets Jest end the case before the promise settles, so the assertion never runs and the test passes. Async failures are silently swallowed and are nearly impossible to spot in CI.
  • Mocks from jest.fn() / jest.spyOn are not auto-cleaned, so reusing one makes toHaveBeenCalledTimes accumulate across tests. Clear or reset them in afterEach.
  • jest.spyOn replaces the method only for the current test; without mockRestore the replacement keeps affecting every later module in the same worker, and the failure points at an unrelated test.
  • An outdated snapshot should be refreshed deliberately with -u. Updating snapshots just to turn the suite green bakes the real behaviour change in along with it.
  • Never restoring real timers after useFakeTimers leaves later tests that depend on setTimeout / setInterval (including third-party init) hanging forever, which shows up as a block of timeouts.
  • A committed test.only makes CI run that single case and skip everything else — a green pipeline no longer says anything about the code.

Tips

  • jest --coverage reports coverage; --watch enables watch mode.
  • jest.mock() auto-mocks all exports, returning jest.fn().
  • toMatchSnapshot suits UI/serialized-structure tests; update with -u.
  • jest.spyOn spies on a method without fully mocking it.
  • useFakeTimers with runAllTimers tests timer logic synchronously.
  • Remove test.only after debugging, or other tests get skipped.

FAQ

My async test passes instantly without ever running the assertion — what happened?

This is the classic false positive: if the test body is neither async nor returns the promise, Jest considers the case finished and passes it immediately, while the rejection escapes as an unhandled error. Two correct forms: test("async", async () => { await fn() }), or test("promise", () => fn().then(d => expect(d).toBe(1))) which explicitly returns so Jest waits. The third is the done-callback form test("done", done => { fn(done) }), where you must call done yourself or the test times out. Prefer asserting on the promise directly with expect(promise).resolves.toBe(value) and expect(promise).rejects.toThrow("err"), which produce far clearer failure output than expect inside then.

How do I verify how and how often a mock was called?

A jest.fn() records every invocation, letting you assert three things: expect(fn).toHaveBeenCalled() confirms it ran at least once, expect(fn).toHaveBeenCalledWith(arg) confirms a call carried those arguments, and expect(fn).toHaveBeenCalledTimes(2) pins the exact count — reach for the last one whenever precision matters. expect(fn).toHaveBeenLastCalledWith(arg) inspects only the final call. Configure return values with mockReturnValue(42), mockResolvedValue(val) or mockImplementation(n => n * 2). Between assertions, use mockClear() to drop the call record, mockReset() to also drop the implementation, and mockRestore() to put a spied method back — otherwise state leaks between tests.

Why does timer-based code never run in my test, and does fake timers alone fix it?

setTimeout and setInterval make tests slow or hang outright. jest.useFakeTimers() swaps them for controllable stand-ins, but you must then drive them yourself — jest.runAllTimers() flushes every pending callback (pair it with jest.advanceTimersByTime(ms) to advance a specific span), otherwise your assertions execute before the callback fires and the test fails. Two conditions matter: enable fake timers before the test starts, and restore real timers afterwards, or every later timing-dependent case will hang. Whether faking works on a third-party library also depends on whether it uses the global setTimeout rather than keeping its own captured reference.

When should I reach for jest.mock versus jest.spyOn?

jest.mock("./module") acts at module granularity: every import of that module is replaced by the automatic mock, whose exports become jest.fn()s that you then configure with mockReturnValue. Use jest.requireActual("./module") when you need the real implementation inside a mocked module. jest.spyOn(obj, "method") is the local tool — it keeps the original behaviour by default, so you can watch calls with expect(spy).toHaveBeenCalled() while still exercising real logic, or override with mockImplementation. Rule of thumb: use spyOn when you mainly need to observe interaction, jest.mock when you need to fully sever the dependency. Remember mockRestore to undo the spy.

When are snapshots worth it, and how do you update them safely?

toMatchSnapshot() serializes the value into a .snap file on first run and compares against it afterwards; it pays off for UI trees and large serialized structures where hand-writing assertions is expensive and error-prone. Update with the -u flag (npx jest -u), ideally combined with test.only and npx jest -t "case name" so you refresh a single snapshot deliberately rather than blindly accepting every change. The main risk is that snapshots can hide regressions, so review the diff before committing. Use toMatchInlineSnapshot() when you want the expectation stored in the test file itself, where a structural change shows up directly in code review.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us