jestjs/jest · error · Error

Cannot replace the `${propertyKey}` property because it has

Error message

Cannot replace the `${propertyKey}` property because it has a setter. Use `jest.spyOn(object, '${propertyKey}', 'set').mockReturnValue(value)` instead.

What it means

Thrown by jest.replaceProperty when the descriptor has a setter (descriptor.set !== undefined). The same rationale as error 155: replaceProperty cannot replace an accessor-backed property, and the message directs you to jest.spyOn(object, 'key', 'set').mockReturnValue(value).

Source

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

    }
    if (!descriptor.configurable) {
      throw new Error(
        `Property \`${String(propertyKey)}\` is not declared configurable`,
      );
    }

    if (descriptor.get !== undefined) {
      throw new Error(
        `Cannot replace the \`${String(
          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.`,
      );
    }

View on GitHub (pinned to f49721c78e)

Solutions

  1. Use jest.spyOn(object, 'key', 'set') to intercept assignments and mockReturnValue if needed.
  2. If both get and set exist, decide which direction you need and spy on that one (or both).
  3. If you truly must replace the whole property, redefine it with Object.defineProperty first (if configurable).

Example fix

// before
jest.replaceProperty(form, 'email', 'a@b.com'); // email has a setter
// after
const spy = jest.spyOn(form, 'email', 'set');
form.email = 'a@b.com';
expect(spy).toHaveBeenCalledWith('a@b.com');
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

const isSetterProp = (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.set === 'function');
};

if (isSetterProp(obj, key)) {
  jest.spyOn(obj, key, 'set');
} else {
  jest.replaceProperty(obj, key, value);
}

Prevention

When it happens

Trigger: jest.replaceProperty(obj, 'value', v) where value is backed by a set accessor (write-only or get/set pair); targeting a property of a class that declared set value(x){...}.

Common situations: Write-only credential setters; classes that validate on assignment via a setter; reactive frameworks that intercept assignment.

Related errors


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