Skip to content

Jest Mock Functions

A mock function is a fake function that automatically records how it was used: how many times it was called, with what arguments, and what it returned. This lesson explores mock functions and the assertions built around them.

What a Mock Function Tracks

Every mock function created with jest.fn() exposes a .mock property containing arrays like calls (the arguments passed on each call) and results (what the function returned or threw each time). You rarely inspect .mock directly — instead, you use expect() matchers built specifically for mock functions.

This tracking happens automatically for every mock, with no extra setup required beyond creating it with jest.fn().

const onSave = jest.fn();

onSave('draft-1');
onSave('draft-2');

expect(onSave).toHaveBeenCalledTimes(2);
expect(onSave).toHaveBeenNthCalledWith(1, 'draft-1');
expect(onSave).toHaveBeenLastCalledWith('draft-2');

Three different mock-function matchers checking call count, a specific call, and the most recent call.

Mock Function Matchers

expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(n);
expect(mockFn).toHaveBeenCalledWith(arg1, arg2);
expect(mockFn).toHaveBeenLastCalledWith(arg1, arg2);
expect(mockFn).toHaveBeenNthCalledWith(n, arg1, arg2);
  • .toHaveBeenCalled() only checks the function was invoked at least once.
  • .toHaveBeenCalledTimes(n) checks the exact number of invocations.
  • .toHaveBeenCalledWith(...) checks at least one call matched these exact arguments.
  • .toHaveBeenNthCalledWith(n, ...) checks the arguments of a specific call by position.

Mock Function Matchers Cheat Sheet

Every matcher for asserting on how a mock function was used.

Matcher Checks
.toHaveBeenCalled() Called at least once
.toHaveBeenCalledTimes(n) Called exactly n times
.toHaveBeenCalledWith(...) At least one call used these arguments
.toHaveBeenLastCalledWith(...) Most recent call used these arguments
.toHaveBeenNthCalledWith(n, ...) The nth call used these arguments
.toHaveReturnedWith(x) At least one call returned this value
mockFn.mock.calls Raw array of arguments for every call

Inspecting mock.calls Directly

For cases the built-in matchers don't cover neatly, you can inspect mockFn.mock.calls directly — it's a plain array of arrays, one entry per call, each containing the arguments passed that time.

const log = jest.fn();

log('start');
log('middle', { step: 2 });
log('end');

expect(log.mock.calls).toHaveLength(3);
expect(log.mock.calls[1]).toEqual(['middle', { step: 2 }]);

Testing Code That Takes a Callback

Mock functions are the natural choice for testing code that accepts a callback, since you can pass a jest.fn() in place of a real callback and assert on how (and whether) it was invoked, without needing any real side effect to happen.

function processItems(items, onEach) {
  items.forEach((item) => onEach(item));
}

test('calls onEach for every item', () => {
  const onEach = jest.fn();
  processItems(['a', 'b', 'c'], onEach);

  expect(onEach).toHaveBeenCalledTimes(3);
  expect(onEach).toHaveBeenNthCalledWith(2, 'b');
});

Common Mistakes

  • Using .toHaveBeenCalled() when you actually need to check specific arguments with .toHaveBeenCalledWith().
  • Forgetting mock call history persists across tests unless cleared, causing count assertions to fail unexpectedly.
  • Manually looping over mock.calls when a built-in matcher already does exactly what's needed.
  • Not testing the arguments passed to a callback, only that it was called at all.

Key Takeaways

  • Every jest.fn() mock automatically tracks its calls, arguments, and return values.
  • A family of toHaveBeenCalled... matchers covers nearly every common assertion need.
  • mockFn.mock.calls gives raw access to call arguments for custom checks.
  • Mock functions are ideal for testing code that accepts a callback.

Pro Tip

Reach for .toHaveBeenCalledWith() far more often than .toHaveBeenCalled() — checking the exact arguments catches far more real bugs than just confirming a function ran.