{"record":{"id":"f4f85881f15d13f6","repo":"jestjs/jest","slug":"spyon-methodname-has-already-been-spied-upo","errorCode":null,"errorMessage":"<spyOn> : ${methodName} has already been spied upon\nUsage: spyOn(<object>, <methodName>)","messagePattern":"<spyOn> : (.+?) has already been spied upon\nUsage: spyOn\\(<object>, <methodName>\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/jest-jasmine2/src/jasmine/spyRegistry.ts","lineNumber":107,"sourceCode":"          getErrorMsg(\n            `could not find an object to spy upon for ${methodName}()`,\n          ),\n        );\n      }\n\n      if (methodName === void 0) {\n        throw new Error(getErrorMsg('No method name supplied'));\n      }\n\n      if (obj[methodName] === void 0) {\n        throw new Error(getErrorMsg(`${methodName}() method does not exist`));\n      }\n\n      if (obj[methodName] && isSpy(obj[methodName])) {\n        if (this.respy) {\n          return obj[methodName];\n        } else {\n          throw new Error(\n            getErrorMsg(`${methodName} has already been spied upon`),\n          );\n        }\n      }\n\n      let descriptor;\n      try {\n        descriptor = Object.getOwnPropertyDescriptor(obj, methodName);\n      } catch {\n        // IE 8 doesn't support `definePropery` on non-DOM nodes\n      }\n\n      if (descriptor && !(descriptor.writable || descriptor.set)) {\n        throw new Error(\n          getErrorMsg(\n            `${methodName} is not declared writable or has no setter`,\n          ),\n        );","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/jestjs/jest/blob/8e6d128e4a278059ecddecaa97400b04c8ae5fd9/packages/jest-jasmine2/src/jasmine/spyRegistry.ts#L89-L125","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nbeforeEach(() => { spyOn(db, 'query'); });\nit('also spies', () => { spyOn(db, 'query'); }); // throws\n// after\nbeforeEach(() => { jest.spyOn(db, 'query'); });\nafterEach(() => jest.restoreAllMocks());","handlingStrategy":"validation","validationCode":"afterEach(() => jest.restoreAllMocks());\n// or check before re-spying\nif (jest.isMockFunction(obj.method)) {\n  (obj.method as jest.Mock).mockRestore();\n}\nspyOn(obj, 'method');","typeGuard":"import {Spy} from 'jest-mock';\nconst isJestSpy = (v: unknown): v is jest.SpyInstance =>\n  v != null && typeof (v as any)._isMockFunction === 'boolean' && (v as any)._isMockFunction;","tryCatchPattern":null,"preventionTips":["Set restoreMocks:true in Jest config.","Consolidate spies into a single beforeEach.","Use jest.restoreAllMocks in afterEach."],"tags":["jest","spyon","mocking","duplicate-spy","test-isolation"],"backgroundTag":null,"analyzedSha":"8e6d128e4a278059ecddecaa97400b04c8ae5fd9","analyzedAt":"2026-08-10T18:11:27.960Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}