{"record":{"id":"a0b03fe50c9926e5","repo":"jestjs/jest","slug":"jasmine-spies-would-overwrite-the-and-and-calls","errorCode":null,"errorMessage":"Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon","messagePattern":"Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/jest-jasmine2/src/jasmine/createSpy.ts","lineNumber":65,"sourceCode":"    },\n  });\n  const callTracker = new CallTracker();\n  const spy: Spy = function (...args) {\n    const callData: Context = {\n      object: this,\n      args: Array.prototype.slice.apply(arguments),\n    };\n\n    callTracker.track(callData);\n    const returnValue = spyStrategy.exec.apply(this, args);\n    callData.returnValue = returnValue;\n\n    return returnValue;\n  };\n\n  for (const prop in originalFn) {\n    if (prop === 'and' || prop === 'calls') {\n      throw new Error(\n        \"Jasmine spies would overwrite the 'and' and 'calls' properties \" +\n          'on the object being spied upon',\n      );\n    }\n\n    spy[prop] = originalFn[prop];\n  }\n\n  spy.and = spyStrategy;\n  spy.calls = callTracker;\n\n  return spy;\n}\n\nexport default createSpy;\n","sourceCodeStart":47,"sourceCodeEnd":81,"githubUrl":"https://github.com/jestjs/jest/blob/8e6d128e4a278059ecddecaa97400b04c8ae5fd9/packages/jest-jasmine2/src/jasmine/createSpy.ts#L47-L81","documentation":"When jasmine creates a spy, it copies enumerable properties from the original function onto the spy, then sets `spy.and` (the strategy) and `spy.calls` (the call tracker). If the original function ALREADY has its own `and` or `calls` property, jasmine would overwrite it and lose data — so createSpy throws this Error at createSpy.ts:65 to prevent silent clobbering.","triggerScenarios":"Calling `jasmine.createSpy(name, fn)` where `fn` has an own property named `and` or `calls`. The loop at createSpy.ts:63 iterates `for (const prop in originalFn)` and checks `prop === 'and' || prop === 'calls'` at line 64.","commonSituations":"Spying on a function that is ITSELF already a spy (double-spying); spying on a function augmented with an `and` or `calls` property by another library; passing an object that masquerades as a function and has those keys; chaining createSpy on the output of createSpy.","solutions":["Do not spy on an existing spy — spy on the original source function once.","Remove or rename the `and`/`calls` property on the target function before spying.","If you need to reset a spy, use `mockClear()`/`mockReset()` instead of re-creating it.","Pass a plain function (without those properties) as originalFn to createSpy."],"exampleFix":"// before\nconst spy1 = jasmine.createSpy('fn', fn);\nconst spy2 = jasmine.createSpy('fn', spy1); // spy1 has .and/.calls → throws\n// after — reuse the existing spy\nconst spy = jasmine.createSpy('fn', fn);\nspy.mockClear(); // reset instead of re-spying","handlingStrategy":"validation","validationCode":"// Before spying, check the target does not already expose and/calls.\nfunction isSafeToSpy(fn: Record<string, unknown>): boolean {\n  return !('and' in fn) && !('calls' in fn);\n}\n// Usage:\nif (isSafeToSpy(targetFn)) {\n  spy = spyOn(obj, 'method');\n} else {\n  // target is already a spy or has conflicting props — reset instead\n}","typeGuard":"function hasReservedProps(fn: Record<string, unknown>): boolean {\n  return 'and' in fn || 'calls' in fn;\n}","tryCatchPattern":null,"preventionTips":["Never spy on an existing spy; use mockReset/mockRestore to reset it.","Check for `.and`/`.calls` properties on the target function before createSpy.","Keep a single spy per source method; track and reuse it across tests."],"tags":["spy","jasmine","property-conflict","createspy","api-misuse"],"backgroundTag":null,"analyzedSha":"8e6d128e4a278059ecddecaa97400b04c8ae5fd9","analyzedAt":"2026-08-10T18:11:27.960Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}