{"id":"c281a64382b44615","repo":"jestjs/jest","slug":"received-value-must-be-a-mock-or-spy-function","errorCode":null,"errorMessage":"received value must be a mock or spy function","messagePattern":"received value must be a mock or spy function","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/expect/src/spyMatchers.ts","lineNumber":1272,"sourceCode":"};\n\nconst isMock = (received: any) =>\n  received != null && received._isMockFunction === true;\n\nconst isSpy = (received: any) =>\n  received != null &&\n  received.calls != null &&\n  typeof received.calls.all === 'function' &&\n  typeof received.calls.count === 'function';\n\nconst ensureMockOrSpy = (\n  received: any,\n  matcherName: string,\n  expectedArgument: string,\n  options: MatcherHintOptions,\n) => {\n  if (!isMock(received) && !isSpy(received)) {\n    throw new Error(\n      matcherErrorMessage(\n        matcherHint(matcherName, undefined, expectedArgument, options),\n        `${RECEIVED_COLOR('received')} value must be a mock or spy function`,\n        printWithType('Received', received, printReceived),\n      ),\n    );\n  }\n};\n\nconst ensureMock = (\n  received: any,\n  matcherName: string,\n  expectedArgument: string,\n  options: MatcherHintOptions,\n) => {\n  if (!isMock(received)) {\n    throw new Error(\n      matcherErrorMessage(","sourceCodeStart":1254,"sourceCodeEnd":1290,"githubUrl":"https://github.com/jestjs/jest/blob/f49721c78e195558b40913977c9230f5b7f559d8/packages/expect/src/spyMatchers.ts#L1254-L1290","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap the function with `jest.fn()`: `const fn = jest.fn();` then pass `fn` into the system under test.","Use `jest.spyOn(obj, 'method')` to spy on an existing method before it is called.","Verify the mock shape: log `received._isMockFunction` — should be `true`.","If using a different fake library, adapt with a custom matcher or convert to `jest.fn()`."],"exampleFix":"// before\nconst send = (x) => x;\nsystem.run(send);\nexpect(send).toHaveBeenCalled(); // not a mock\n\n// after\nconst send = jest.fn();\nsystem.run(send);\nexpect(send).toHaveBeenCalled();","handlingStrategy":"type-guard","validationCode":"if (received == null || received._isMockFunction !== true && !(received.calls?.all instanceof Function)) {\n  throw new Error('received must be a jest.fn() mock or jest.spyOn() spy');\n}\nexpect(received).toHaveBeenCalled();","typeGuard":"const isMockOrSpy = (v: unknown): boolean =>\n  v != null &&\n  ((v as any)._isMockFunction === true ||\n    (typeof (v as any).calls?.all === 'function' &&\n      typeof (v as any).calls?.count === 'function'));","tryCatchPattern":"try {\n  expect(received).toHaveBeenCalled();\n} catch (e) {\n  if (e instanceof Error && /must be a mock or spy function/.test(e.message)) {\n    console.error('received is not a mock/spy:', received);\n  }\n  throw e;\n}","preventionTips":["Create mocks with `jest.fn()` and pass them into the system under test.","Spy on existing methods with `jest.spyOn(obj, 'method')` before they are called.","Verify `received._isMockFunction === true` before asserting in dynamic test setups."],"tags":["jest","expect","mock","spy","assertion","configuration"],"analyzedSha":"f49721c78e195558b40913977c9230f5b7f559d8","analyzedAt":"2026-08-03T20:16:28.571Z","schemaVersion":2}