jestjs/jest · error · Error
${propertyName} has already been spied upon
Error message
${propertyName} has already been spied upon What it means
Thrown by `SpyRegistry._spyOnProperty` in spyRegistry.ts:195 when the property value is already a jasmine spy and `respy` is not enabled. Same double-spy protection as the method variant, applied to property accessors.
Source
Thrown at packages/jest-jasmine2/src/jasmine/spyRegistry.ts:195
if (!descriptor.configurable) {
throw new Error(
getErrorMsg(`${propertyName} is not declared configurable`),
);
}
if (!descriptor[accessType]) {
throw new Error(
getErrorMsg(
`Property ${propertyName} does not have access type ${accessType}`,
),
);
}
if (obj[propertyName] && isSpy(obj[propertyName])) {
if (this.respy) {
return obj[propertyName];
} else {
throw new Error(
getErrorMsg(`${propertyName} has already been spied upon`),
);
}
}
const originalDescriptor = descriptor;
const spiedProperty = createSpy(propertyName, descriptor[accessType]);
let restoreStrategy;
if (Object.prototype.hasOwnProperty.call(obj, propertyName)) {
restoreStrategy = function () {
Object.defineProperty(obj, propertyName, originalDescriptor);
};
} else {
restoreStrategy = function () {
delete obj[propertyName];
};
}View on GitHub (pinned to f49721c78e)
Solutions
- Spy once — remove the duplicate `spyOnProperty` call.
- Enable respy via `jasmine.getEnv().allowRespy(true)` if re-spying is intentional.
- Ensure isolation so the prior spy is restored before the next attempt.
Example fix
// before
beforeEach(() => spyOnProperty(cfg, 'env', 'get'));
it('x', () => { spyOnProperty(cfg, 'env', 'get'); /* throws */ });
// after
beforeEach(() => spyOnProperty(cfg, 'env', 'get'));
it('x', () => { /* reuse the existing spy */ }); Defensive patterns
Strategy: validation
Validate before calling
const cur = obj[propertyName];
if (cur && cur.and && cur.calls) { /* already a spy */ }
else spyOnProperty(obj, propertyName, 'get'); Type guard
const isSpy = (v: any): boolean => v != null && v.and != null && typeof v.and === 'object' && v.calls != null;
Prevention
- Register property spies in a single beforeEach.
- Enable allowRespy(true) when re-spying is intentional.
When it happens
Trigger: Calling `spyOnProperty(obj, 'p', 'get')` twice in the same test, or once in `beforeEach` and again in the test body.
Common situations: Shared `beforeEach` plus a per-test spy; helper that spies unconditionally; module-level object whose spies are not restored between tests.
Related errors
- ${methodName} has already been spied upon
- could not find an object to spy upon for ${propertyName}
- No property name supplied
- ${propertyName} property does not exist
- ${propertyName} is not declared configurable
AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03).
Data as JSON: /data/errors/2d98723865e0dcc4.json.
Report an issue: GitHub.