jestjs/jest · error · Error
<spyOn> : ${methodName} has already been spied upon Usage: s
Error message
<spyOn> : ${methodName} has already been spied upon
Usage: spyOn(<object>, <methodName>) What it means
Thrown by SpyRegistry.spyOn when the target method is already a spy and respy is not enabled (spyRegistry.ts:107). SpyRegistry only allows re-spying when allowRespy('true') has been called; otherwise a second spyOn on the same method would clobber the original reference and break restoration.
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 8e6d128e4a)
Solutions
- Call jest.restoreAllMocks() in an afterEach to clear spies between tests, or jest.clearAllMocks() if you only need call data reset.
- Consolidate the spy into a single location (one beforeEach) and remove duplicates.
- If you genuinely need re-spying, opt in via jasmine.getEnv().allowRespy(true) (advanced, can mask real bugs).
- Use jest.spyOn within beforeEach with restoreMocks:true in config so each test starts clean.
Example fix
// before
beforeEach(() => { spyOn(db, 'query'); });
it('also spies', () => { spyOn(db, 'query'); }); // throws
// after
beforeEach(() => { jest.spyOn(db, 'query'); });
afterEach(() => jest.restoreAllMocks()); Defensive patterns
Strategy: validation
Validate before calling
afterEach(() => jest.restoreAllMocks());
// or check before re-spying
if (jest.isMockFunction(obj.method)) {
(obj.method as jest.Mock).mockRestore();
}
spyOn(obj, 'method'); Type guard
import {Spy} from 'jest-mock';
const isJestSpy = (v: unknown): v is jest.SpyInstance =>
v != null && typeof (v as any)._isMockFunction === 'boolean' && (v as any)._isMockFunction; Prevention
- Set restoreMocks:true in Jest config.
- Consolidate spies into a single beforeEach.
- Use jest.restoreAllMocks in afterEach.
When it happens
Trigger: spyOn(obj, 'm') called twice in the same file, two beforeEach blocks that both spy on the same method, a custom matcher that internally spies on a method already spied by the test, or a helper that wraps spyOn without checking.
Common situations: Shared beforeEach spies combined with per-test spies; refactoring that moved a spy into a utility imported by multiple specs; multiple test files that share global setup; auto-generated test scaffolding that double-applies spies.
Related errors
- ${propertyName} has already been spied upon
- <spyOn> : could not find an object to spy upon for ${methodN
- <spyOn> : No method name supplied Usage: spyOn(<object>, <me
- <spyOn> : ${methodName}() method does not exist Usage: spyOn
- <spyOn> : ${methodName} is not declared writable or has no s
AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10).
Data as JSON: /api/errors/f4f85881f15d13f6.
Report an issue: GitHub.