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
- Move the `spyOn(...)` call inside a `beforeEach`, `beforeAll`, or an `it` body.
- If the spy must exist for the whole suite, wrap it in `beforeAll(() => { spyOn(...) })`.
- 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
- Always call spyOn inside beforeEach, beforeAll, or an it body.
- Use eslint-plugin-jest rules to catch top-level spy calls.
- Keep test helper modules free of side-effecting spyOn calls at import time.
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
- Argument passed to callFake should be a function, got
- Jasmine spies would overwrite the 'and' and 'calls'…
- Missing second argument. It must be a callback function.
- Tests cannot be nested. Test
- Cannot replace the ` ` property because it is a function…
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)