Cover des Artikels

Jest, TypeScript und Mock-Scopes: Warum dein undefined nicht zurückkommt

Jest richtig verstehen: So funktionieren describe-, beforeEach- und Mock-Scopes in TypeScript – und warum dein Mock kein undefined liefert.

Frank Seidinger
Frank Seidinger
6. Nov. 2025Lesedauer 1 Min

Beitrag teilen auf

Jest, TypeScript und Mock-Scopes: Warum dein undefined nicht zurückkommt

Beim Schreiben von Unit-Tests in TypeScript mit Jest stolpert man häufig über ein merkwürdiges Verhalten: Man setzt einen Mock auf undefined, aber der Test liefert trotzdem ein Objekt. Das Problem liegt nicht im Mock selbst, sondern im Scope und der Lebenszeit von Variablen und Mock-Rückgaben.

1. Der typische Fehler

Viele schreiben Mocks direkt in einem describe-Block:

describe("Given a user", () => {
  describe("That is logged out", () => {
    sessionMock.mockReturnValue(undefined);

    it("recognizes the user as logged out", async () => {
      const result = await isLoggedIn();
      expect(result).toBe(false);
    });
  });
  
  describe("That is logged in as a user", () => {
    sessionMock.mockReturnValue({
      session: {},
      user: {
        role: "user"
      }
    });
  
    it("recognizes the user as logged in", async () => {
      const result: boolean = await isLoggedIn();
      expect(result).toBe(true);
    });
  });
});

Intuitiv könnte man denken: „Beim Betreten des Describe-Blocks wird der Mock gesetzt.“ Tatsächlich wird der gesamte Code im Describe-Block nur einmal beim Laden des Moduls ausgeführt, nicht vor jedem Test. Das kann dazu führen, dass vorherige Mocks oder andere Initialisierungen den Wert überschreiben.

2. Richtiger Umgang mit Mocks in Jest

Verwende beforeEach für dynamische Rückgaben.

describe("That is logged out", () => {
  beforeEach(() => {
    // Async-Funktion korrekt mocken
    sessionMock.mockResolvedValue(undefined);
  });

  it("recognizes the user as logged out", async () => {
    const result = await isLoggedIn();
    expect(result).toBe(false);
  });
});

Für einen „eingeloggten“ Nutzer:

describe("Logged in as a user", () => {
  beforeEach(() => {
    sessionMock.mockResolvedValue({
      session: {},
      user: { role: "patient" },
    });
  });

  it("recognizes the user as logged in", async () => {
    const result = await isLoggedIn();
    expect(result).toBe(true);
  });
});

Tipp: Für async-Funktionen immer mockResolvedValue() statt mockReturnValue() verwenden, sonst bekommst du nur ein ungelöstes Promise.

3. Jest-Test-Lebenszyklus im Überblick

Phase Wann ausgeführt?
describe Einmal beim Laden der Test-Datei
beforeAll Einmal vor allen Tests in describe
beforeEach Vor jedem it / Test
it / test Jeder einzelne Test
afterEach Nach jedem Test
afterAll Einmal nach allen Tests in describe

So wird klar, warum ein Mock, der direkt in describe gesetzt wird, nicht für jeden Test neu gilt.

4. Fazit

  • Nie Mocks direkt in describe setzen, wenn sie sich zwischen Tests ändern.
  • beforeEach ist dein Freund – hier kannst du Mock-Werte sauber vorbereiten.
  • Async-Funktionen richtig mocken mit mockResolvedValue().
  • Jest-Testlauf: describe wird einmal ausgeführt, it-Blöcke mehrfach – der Scope entscheidet, welche Werte sichtbar sind.
  • Mit diesem Verständnis vermeidest du typische Stolperfallen und stellst sicher, dass deine Tests deterministisch laufen.