jestjs/jest · error · Error

Jasmine spies would overwrite the 'and' and 'calls' properti

Error message

Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon

What it means

createSpy() copies enumerable properties from the original function onto the spy, then assigns the spy's own `and` (SpyStrategy) and `calls` (CallTracker) properties. If the original function already has an `and` or `calls` own property, copying would clobber them — so createSpy throws to avoid silently overwriting user data and breaking the spy API.

Source

Thrown at packages/jest-jasmine2/src/jasmine/createSpy.ts:65

    },
  });
  const callTracker = new CallTracker();
  const spy: Spy = function (...args) {
    const callData: Context = {
      object: this,
      args: Array.prototype.slice.apply(arguments),
    };

    callTracker.track(callData);
    const returnValue = spyStrategy.exec.apply(this, args);
    callData.returnValue = returnValue;

    return returnValue;
  };

  for (const prop in originalFn) {
    if (prop === 'and' || prop === 'calls') {
      throw new Error(
        "Jasmine spies would overwrite the 'and' and 'calls' properties " +
          'on the object being spied upon',
      );
    }

    spy[prop] = originalFn[prop];
  }

  spy.and = spyStrategy;
  spy.calls = callTracker;

  return spy;
}

export default createSpy;

View on GitHub (pinned to f49721c78e)

Solutions

  1. Restore the previous spy (spy.mockRestore() / mockClear()) before spying again so the function no longer carries and/calls.
  2. Spy on a fresh copy of the function instead of an already-decorated one.
  3. Audit for double-spying: search for repeated spyOn on the same object/method across setup files.
  4. If you control the original function, rename its and/calls properties to avoid the reserved names.

Example fix

// before — re-spying a function that already has .and/.calls
const s1 = jest.spyOn(obj, 'fn');
const s2 = jest.spyOn(obj, 'fn'); // createSpy sees existing .and/.calls -> throws

// after — restore before re-spying
const s1 = jest.spyOn(obj, 'fn');
s1.mockRestore();
const s2 = jest.spyOn(obj, 'fn');
Defensive patterns

Strategy: type-guard

Validate before calling

function canSpy(fn: { and?: unknown; calls?: unknown }): boolean {
  return !('and' in fn) && !('calls' in fn);
}
if (!canSpy(originalFn)) {
  throw new Error('target already has and/calls properties; restore the previous spy first');
}

Type guard

function isSpyable(fn: object): boolean {
  const own = Object.getOwnPropertyNames(fn);
  return !own.includes('and') && !own.includes('calls');
}

Prevention

When it happens

Trigger: Calling createSpy(name, originalFn) where originalFn has an own property named 'and' or 'calls' (createSpy.ts:63-69). This happens when spying on a function that was already spied upon, or a function decorated with its own and/calls bookkeeping.

Common situations: Re-spying an already-spied function ( Jasmine spy already installed .and/.calls); spying on a method that another library decorated with and/calls; double jest.spyOn on the same target without restore; spying on a function returned by another spy factory.

Related errors


AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03). Data as JSON: /data/errors/a0b03fe50c9926e5.json. Report an issue: GitHub.