jestjs/jest · error · Error
${methodName} has already been spied upon
Error message
${methodName} has already been spied upon What it means
Thrown by `SpyRegistry.spyOn` in spyRegistry.ts:107 when the method is already a jasmine spy (`isSpy` returns true) and `respy` is not enabled. jasmine2 forbids double-spying by default to avoid losing the original reference and producing confusing call counts.
Source
Thrown at packages/jest-jasmine2/src/jasmine/spyRegistry.ts:107
getErrorMsg(
`could not find an object to spy upon for ${methodName}()`,
),
);
}
if (methodName === void 0) {
throw new Error(getErrorMsg('No method name supplied'));
}
if (obj[methodName] === void 0) {
throw new Error(getErrorMsg(`${methodName}() method does not exist`));
}
if (obj[methodName] && isSpy(obj[methodName])) {
if (this.respy) {
return obj[methodName];
} else {
throw new Error(
getErrorMsg(`${methodName} has already been spied upon`),
);
}
}
let descriptor;
try {
descriptor = Object.getOwnPropertyDescriptor(obj, methodName);
} catch {
// IE 8 doesn't support `definePropery` on non-DOM nodes
}
if (descriptor && !(descriptor.writable || descriptor.set)) {
throw new Error(
getErrorMsg(
`${methodName} is not declared writable or has no setter`,
),
);View on GitHub (pinned to f49721c78e)
Solutions
- Spy once — remove the duplicate `spyOn` call.
- If you genuinely need to re-spy, enable respy via `jasmine.getEnv().allowRespy(true)` (or `spyOnProperty` equivalent) before the second spy.
- Ensure tests are isolated so the previous spy is restored before the next spy attempt.
Example fix
// before
beforeEach(() => spyOn(db, 'query'));
it('x', () => { spyOn(db, 'query'); /* throws */ });
// after
beforeEach(() => spyOn(db, 'query'));
it('x', () => { db.query.mockImplementation(() => 1); }); Defensive patterns
Strategy: validation
Validate before calling
if (obj[methodName] && obj[methodName].and instanceof Object) { /* already a spy, skip or respy */ }
else spyOn(obj, methodName); Type guard
const isSpy = (v: any): boolean => v != null && v.and != null && typeof v.and === 'object' && v.calls != null;
Prevention
- Centralize spies in one beforeEach to avoid duplicate registrations.
- Enable allowRespy(true) only when re-spying is intentional.
When it happens
Trigger: Calling `spyOn(obj, 'm')` twice in the same test, or in a `beforeEach` that runs for a test which also spies; sharing the object across tests without letting jasmine restore it.
Common situations: A shared `beforeEach` spies and an individual test spies again; helper that spies unconditionally and is called from multiple setup hooks; tests not isolated because the object is module-level and spies accumulate.
Related errors
- ${propertyName} has already been spied upon
- could not find an object to spy upon for ${methodName}()
- No method name supplied
- ${methodName}() method does not exist
- ${methodName} is not declared writable or has no setter
AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03).
Data as JSON: /data/errors/13d68d571d3c47e0.json.
Report an issue: GitHub.