jestjs/jest · error · TypeError

Cannot replace the `${propertyKey}` property because it is a

Error message

Cannot replace the `${propertyKey}` property because it is a function. Use `jest.spyOn(object, '${propertyKey}')` instead.

What it means

Thrown as a TypeError by jest.replaceProperty when descriptor.value is a function. Replacing a method with a constant value loses the function contract and breaks callers; the correct tool is spyOn, which wraps the function so calls are recorded and a mock implementation can be supplied. The message tells you to use jest.spyOn(object, 'key') instead.

Source

Thrown at packages/jest-mock/src/index.ts:1510

          propertyKey,
        )}\` property because it has a getter. Use \`jest.spyOn(object, '${String(
          propertyKey,
        )}', 'get').mockReturnValue(value)\` instead.`,
      );
    }

    if (descriptor.set !== undefined) {
      throw new Error(
        `Cannot replace the \`${String(
          propertyKey,
        )}\` property because it has a setter. Use \`jest.spyOn(object, '${String(
          propertyKey,
        )}', 'set').mockReturnValue(value)\` instead.`,
      );
    }

    if (typeof descriptor.value === 'function') {
      throw new TypeError(
        `Cannot replace the \`${String(
          propertyKey,
        )}\` property because it is a function. Use \`jest.spyOn(object, '${String(
          propertyKey,
        )}')\` instead.`,
      );
    }

    const existingRestore = this._findReplacedProperty(object, propertyKey);

    if (existingRestore) {
      return existingRestore.replaced.replaceValue(value);
    }

    const isPropertyOwner = Object.prototype.hasOwnProperty.call(
      object,
      propertyKey,
    );

View on GitHub (pinned to f49721c78e)

Solutions

  1. Switch to jest.spyOn(object, 'key').mockImplementation(fn) or mockResolvedValue for async.
  2. If you genuinely need to replace the function entirely with a fresh mock, use jest.spyOn(...).mockReturnValue(...) or assign via spyOn and mockImplementation.

Example fix

// before
jest.replaceProperty(api, 'fetch', mockFetch); // api.fetch is a function
// after
jest.spyOn(api, 'fetch').mockImplementation(mockFetch);
Defensive patterns

Strategy: type-guard

Validate before calling

const d = Object.getOwnPropertyDescriptor(obj, key) ?? Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), key);
if (d && typeof d.value === 'function') {
  jest.spyOn(obj, key).mockImplementation(newValue);
} else {
  jest.replaceProperty(obj, key, newValue);
}

Type guard

const isMethodValue = (o: object, k: PropertyKey) => {
  let d = Object.getOwnPropertyDescriptor(o, k);
  let p = Object.getPrototypeOf(o);
  while (!d && p) { d = Object.getOwnPropertyDescriptor(p, k); p = Object.getPrototypeOf(p); }
  return !!(d && typeof d.value === 'function');
};

if (isMethodValue(obj, key)) {
  jest.spyOn(obj, key);
} else {
  jest.replaceProperty(obj, key, value);
}

Prevention

When it happens

Trigger: jest.replaceProperty(api, 'fetch', () => {}) where api.fetch is a function; replacing any method with a non-function value; confusing replaceProperty (for data) with spyOn (for methods).

Common situations: Copy-pasting a replaceProperty call where a method needed spying; refactor that turned a data property into a method without updating tests.

Related errors


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