jestjs/jest · error · Error

received value must be a mock or spy function

Error message

received value must be a mock or spy function

What it means

Thrown by `ensureMockOrSpy` (spyMatchers.ts:1265), invoked from `toHaveBeenCalled`, `toHaveBeenCalledTimes`, `toHaveBeenCalledWith`, `toHaveBeenLastCalledWith`, and `toHaveBeenNthCalledWith`. It fires when the received value is neither a Jest mock function (`_isMockFunction === true`) nor a Jasmine-style spy (has `.calls.all` and `.calls.count` functions). These matchers read `received.mock.calls` or `received.calls`, which only exist on mocks/spies; a plain function or any other value would crash on property access.

Source

Thrown at packages/expect/src/spyMatchers.ts:1272

};

const isMock = (received: any) =>
  received != null && received._isMockFunction === true;

const isSpy = (received: any) =>
  received != null &&
  received.calls != null &&
  typeof received.calls.all === 'function' &&
  typeof received.calls.count === 'function';

const ensureMockOrSpy = (
  received: any,
  matcherName: string,
  expectedArgument: string,
  options: MatcherHintOptions,
) => {
  if (!isMock(received) && !isSpy(received)) {
    throw new Error(
      matcherErrorMessage(
        matcherHint(matcherName, undefined, expectedArgument, options),
        `${RECEIVED_COLOR('received')} value must be a mock or spy function`,
        printWithType('Received', received, printReceived),
      ),
    );
  }
};

const ensureMock = (
  received: any,
  matcherName: string,
  expectedArgument: string,
  options: MatcherHintOptions,
) => {
  if (!isMock(received)) {
    throw new Error(
      matcherErrorMessage(

View on GitHub (pinned to f49721c78e)

Solutions

  1. Wrap the function with `jest.fn()`: `const fn = jest.fn();` then pass `fn` into the system under test.
  2. Use `jest.spyOn(obj, 'method')` to spy on an existing method before it is called.
  3. Verify the mock shape: log `received._isMockFunction` — should be `true`.
  4. If using a different fake library, adapt with a custom matcher or convert to `jest.fn()`.

Example fix

// before
const send = (x) => x;
system.run(send);
expect(send).toHaveBeenCalled(); // not a mock

// after
const send = jest.fn();
system.run(send);
expect(send).toHaveBeenCalled();
Defensive patterns

Strategy: type-guard

Validate before calling

if (received == null || received._isMockFunction !== true && !(received.calls?.all instanceof Function)) {
  throw new Error('received must be a jest.fn() mock or jest.spyOn() spy');
}
expect(received).toHaveBeenCalled();

Type guard

const isMockOrSpy = (v: unknown): boolean =>
  v != null &&
  ((v as any)._isMockFunction === true ||
    (typeof (v as any).calls?.all === 'function' &&
      typeof (v as any).calls?.count === 'function'));

Try / catch

try {
  expect(received).toHaveBeenCalled();
} catch (e) {
  if (e instanceof Error && /must be a mock or spy function/.test(e.message)) {
    console.error('received is not a mock/spy:', received);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `expect(plainFn).toHaveBeenCalled()` where `plainFn` is a regular function (not wrapped with `jest.fn()`); `expect(callback).toHaveBeenCalledWith(...)` after passing a real implementation instead of a mock; `expect(obj.method).toHaveBeenCalled()` where `obj.method` was not spied on with `jest.spyOn(obj, 'method')`; passing a mock from a different test/library (vitest, sinon) that lacks the Jest mock shape.

Common situations: Forgetting to replace an injected dependency with `jest.fn()`; using `jest.spyOn` on a non-existent method; the spy was restored before the assertion; mixing Jest with another test framework's fakes; refactoring an injection such that the real implementation flows through.

Related errors


AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03). Data as JSON: /data/errors/c281a64382b44615.json. Report an issue: GitHub.