jestjs/jest · error · TypeError
Cannot spy on the `${methodKey}` property because it is not
Error message
Cannot spy on the `${methodKey}` property because it is not a function; ${typeOfOriginal} given instead. What it means
Thrown as a TypeError by jest.spyOn when object[methodKey] exists and is truthy but typeof original !== 'function' (and it is not already a mock). spyOn only works on methods; for value properties you must use replaceProperty. The message is helpful: it names the actual type found and, for non-object values, suggests jest.replaceProperty(object, 'key', value) instead.
Source
Thrown at packages/jest-mock/src/index.ts:1305
}
if (accessType) {
return this._spyOnProperty(object, methodKey, accessType);
}
const original = object[methodKey];
if (!original) {
throw new Error(
`Property \`${String(
methodKey,
)}\` does not exist in the provided object`,
);
}
if (!this.isMockFunction(original)) {
if (typeof original !== 'function') {
throw new TypeError(
`Cannot spy on the \`${String(
methodKey,
)}\` property because it is not a function; ${this._typeOf(
original,
)} given instead.${
typeof original === 'object'
? ''
: ` If you are trying to mock a property, use \`jest.replaceProperty(object, '${String(
methodKey,
)}', value)\` instead.`
}`,
);
}
const isMethodOwner = Object.prototype.hasOwnProperty.call(
object,
methodKey,
);View on GitHub (pinned to f49721c78e)
Solutions
- Use jest.replaceProperty(object, 'key', value) for value properties, then restore with .restore().
- If the property should be a method, fix the producer so it actually assigns a function.
- If you meant to intercept a getter, call jest.spyOn(object, 'key', 'get').
Example fix
// before jest.spyOn(config, 'port'); // port is 3000 // after jest.replaceProperty(config, 'port', 8080);
Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof obj[methodKey] !== 'function') {
// It's a value property -> use replaceProperty
jest.replaceProperty(obj, methodKey, newValue);
} else {
jest.spyOn(obj, methodKey);
} Type guard
const isMethod = <T extends object, K extends keyof T>(o: T, k: K): o is T & Record<K, (...a: any[]) => any> =>
typeof o[k] === 'function';
if (isMethod(obj, 'port')) {
jest.spyOn(obj, 'port');
} else {
jest.replaceProperty(obj, 'port', 8080);
} Prevention
- Use replaceProperty for data, spyOn for methods.
- When refactoring a property from function to value, update tests immediately.
- Inspect typeof obj.key before deciding which API to call.
When it happens
Trigger: jest.spyOn(config, 'port') where config.port = 3000; jest.spyOn(user, 'name') where name is a string; jest.spyOn(arr, 'length') (length is a number); jest.spyOn(obj, 'list') where list is an array. The mocker refuses to wrap a non-function because there is no invocation to intercept.
Common situations: Confusing a property that holds data with a method that returns data; using spyOn where replaceProperty is the right tool; spying on a field whose value changed from a function to a constant after a refactor.
Related errors
- Cannot replace the `${propertyKey}` property because it is a
- Cannot use spyOn on a primitive value; ${typeOfObject} given
- Cannot spy on the ${propertyKey} property because it is not
- Cannot use replaceProperty on a primitive value; ${typeOfObj
- No property name supplied
AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03).
Data as JSON: /data/errors/79c77b3eac6709d4.json.
Report an issue: GitHub.