jestjs/jest · error · Error

${RECEIVED_COLOR('received')} value must be a mock or spy fu

Error message

${RECEIVED_COLOR('received')} value must be a mock or spy function

What it means

Thrown by `ensureMockOrSpy` (spyMatchers.ts:1271), used by toHaveBeenCalled, toHaveBeenCalledTimes, toHaveBeenCalledWith, toHaveBeenLastCalledWith, and toHaveBeenNthCalledWith. It fires when the received value is neither a Jest mock (`_isMockFunction === true`) nor a Jasmine-style spy (has `.calls.all` and `.calls.count`). The call-counting matchers need `received.mock.calls`/`received.calls`, so a plain function is rejected.

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 8e6d128e4a)

Solutions

  1. Create the function with jest.fn(): const fn = jest.fn(); then assert on fn.
  2. Spy on an existing method: const spy = jest.spyOn(obj, 'method'); then expect(spy).toHaveBeenCalled().
  3. Ensure you import/require the module fresh so the spy attaches to the instance actually used by the code under test.
  4. Verify the variable holds the mock, not the original (a common bug: spying then passing the wrong reference).

Example fix

// before
const handler = () => {};
doWork(handler);
expect(handler).toHaveBeenCalled(); // plain fn

// after
const handler = jest.fn();
doWork(handler);
expect(handler).toHaveBeenCalled();
// spying
const spy = jest.spyOn(service, 'save');
service.save();
expect(spy).toHaveBeenCalled();
Defensive patterns

Strategy: type-guard

Validate before calling

const fn = received;
if (!(fn != null && fn._isMockFunction === true) &&
    !(fn != null && typeof fn.calls?.all === 'function')) {
  throw new Error('call matchers require a jest.fn() or jest.spyOn() result');
}
expect(fn).toHaveBeenCalled();

Type guard

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

Prevention

When it happens

Trigger: Calling `expect(plainFn).toHaveBeenCalled()` where plainFn is a normal function, an arrow function, undefined, a method reference that was not spied, or a function wrapped with a custom decorator instead of jest.fn()/jest.spyOn().

Common situations: Forgot to wrap with jest.fn() or jest.spyOn; spying on a method that doesn't exist on the object (spyOn returns nothing usable); the function was reassigned/lost its mock after a module re-import; testing a real implementation instead of the mock.

Related errors


AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10). Data as JSON: /api/errors/0cdee369173b1a3e. Report an issue: GitHub.