Mock IntersectionObserver in Jest Setup

4 min read.
Tags: jestunit-testjavascript

If your test crashes with IntersectionObserver is not defined, you need a Jest mock for IntersectionObserver. JSDOM does not implement it, so components that use lazy loading, animation, or infinite scroll fail during render.

Why IntersectionObserver fails in Jest

Jest usually runs in a Node + JSDOM environment. You get window, document, and basic DOM APIs, but not every browser API.

IntersectionObserver is one of the missing ones, so this line inside your component is enough to break tests:

new IntersectionObserver(() => {});

Add a global mock in jest.setup.ts

Create or update your setup file:

class MockIntersectionObserver implements IntersectionObserver {
  readonly root: Element | Document | null = null;
  readonly rootMargin = "0px";
  readonly thresholds = [0];

  disconnect(): void {}
  observe(_target: Element): void {}
  takeRecords(): IntersectionObserverEntry[] {
    return [];
  }
  unobserve(_target: Element): void {}
}

Object.defineProperty(window, "IntersectionObserver", {
  writable: true,
  configurable: true,
  value: MockIntersectionObserver,
});

Object.defineProperty(globalThis, "IntersectionObserver", {
  writable: true,
  configurable: true,
  value: MockIntersectionObserver,
});

Then make sure Jest loads it:

// jest.config.ts
import type { Config } from "jest";

const config: Config = {
  testEnvironment: "jsdom",
  setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
};

export default config;

That is enough for most cases.

Optional: inspect calls in tests

If you want to assert that observe was called, use spies:

const observe = jest.fn();
const unobserve = jest.fn();
const disconnect = jest.fn();

class MockIntersectionObserver implements IntersectionObserver {
  readonly root: Element | Document | null = null;
  readonly rootMargin = "0px";
  readonly thresholds = [0];

  disconnect = disconnect;
  observe = observe;
  takeRecords(): IntersectionObserverEntry[] {
    return [];
  }
  unobserve = unobserve;
}

Now you can do assertions like:

expect(observe).toHaveBeenCalled();

Common mistake

Do not put this mock inside one test file unless it is really test-specific. Place it in jest.setup.ts so every test suite gets the same environment.

Conclusion

The fastest fix for IntersectionObserver is not defined in Jest is a global IntersectionObserver mock in jest.setup.ts. Keep it minimal, then add spies only when your test actually needs behavior checks.

Related Posts