jestjs/jest · error · Error

Spies must be created in a before function or a spec

Error message

Spies must be created in a before function or a spec

What it means

Jasmine's spy registry (spyOn) only tracks spies that belong to a currently-executing runnable (a spec, or a before/beforeAll/afterAll hook). If you call `spyOn(...)` at the top level of a test file — outside any `it`/`beforeEach`/`beforeAll` — there is no current runnable, so the `currentSpies()` callback throws this Error. Spies must be registered within a hook or spec body so they can be cleaned up afterward.

Solutions

  1. Move the `spyOn(...)` call inside a `beforeEach`, `beforeAll`, or an `it` body.
  2. If the spy must exist for the whole suite, wrap it in `beforeAll(() => { spyOn(...) })`.
  3. Audit test helpers to ensure they are invoked from within a hook or spec, not at module top level.

Example fix

// before
import { foo } from './foo';
spyOn(foo, 'bar'); // top-level → throws
describe('x', () => { it('works', () => {}) });
// after
describe('x', () => {
  beforeEach(() => { spyOn(foo, 'bar'); });
  it('works', () => {});
});
Defensive patterns

Strategy: validation

Validate before calling

// In a setupFile, detect top-level spyOn misuse via a lint rule or
// by wrapping spyOn to check for an active runnable.
// ESLint rule (eslint-plugin-jest): 'no-standalone-expect' / 'no-spy-at-toplevel'
// ensures spies are inside hooks or specs.

Prevention

When it happens

Trigger: In Env.ts, the SpyRegistry's `currentSpies()` getter (lines 363-370) checks `currentRunnable()`. If null (no spec currently running and no suite executing hooks), it throws. This happens when spyOn is called during module-load/top-level scope, or inside a describe block directly (not inside a before* hook).

Common situations: Calling `spyOn(obj, 'method')` directly inside a `describe(...)` body instead of inside `beforeEach`/`beforeAll`; refactoring a test and accidentally moving a spy outside its hook; using a shared helper that calls spyOn at import time.

Related errors


AI-assisted analysis of jestjs/jest@8e6d128e4a (2026-08-10). Data as JSON: /api/errors/0d031b7d39e6dda1. Report an issue: GitHub.

Appendix: source

Thrown at packages/jest-jasmine2/src/jasmine/Env.ts:365

        }
      };

      this.addReporter = function (reporterToAdd) {
        reporter.addReporter(reporterToAdd);
      };

      this.provideFallbackReporter = function (reporterToAdd) {
        reporter.provideFallbackReporter(reporterToAdd);
      };

      this.clearReporters = function () {
        reporter.clearReporters();
      };

      const spyRegistry = new j$.SpyRegistry({
        currentSpies() {
          if (!currentRunnable()) {
            throw new Error(
              'Spies must be created in a before function or a spec',
            );
          }
          return runnableResources[currentRunnable().id].spies;
        },
      });

      this.allowRespy = function (allow) {
        spyRegistry.allowRespy(allow);
      };

      this.spyOn = function (...args) {
        return spyRegistry.spyOn.apply(spyRegistry, args);
      };

      const suiteFactory = function (description: Circus.TestNameLike) {
        const suite = new j$.Suite({
          id: getNextSuiteId(),

View on GitHub (pinned to 8e6d128e4a)