msw TypeScript: Komplettanleitung mit Beispielen

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

msw für TypeScript: Komplettanleitung mit Beispielen

Was ist msw und wozu braucht man es?

Mock Service Worker (msw) ist eine Bibliothek zum Abfangen und Mocken von Netzwerkanfragen auf der Ebene der Service Worker API. Im Gegensatz zu traditionellen Lösungen (wie axios-mock-adapter oder nock) arbeitet msw auf Browser- oder Node.js-Ebene, fängt echte HTTP-Anfragen ab und gibt vordefinierte Antworten zurück. So können Anwendungen getestet werden, ohne einen echten Server starten oder den Anwendungscode ändern zu müssen.

Das Hauptziel von msw ist es, ein einheitliches Werkzeug zum Mocken von APIs sowohl in Browser-Tests (mit einem echten Service Worker) als auch serverseitig (Node.js) bereitzustellen. Die Bibliothek ist besonders nützlich für Entwickler, die TypeScript verwenden, da sie eine strenge Typisierung für alle Anfragen und Antworten bietet. msw eignet sich ideal für Unit-Tests, Integrationstests und die Frontend-Entwicklung in Isolation vom Backend.

Der entscheidende Vorteil von msw ist, dass die Mocks auf Netzwerkebene und nicht auf Codeebene arbeiten. Das bedeutet, dass alle Bibliotheken für HTTP-Anfragen (fetch, axios, graphql-request usw.) automatisch abgefangen werden, ohne zusätzliche Konfiguration. Die Bibliothek unterstützt REST APIs, GraphQL und kann über Erweiterungen auch WebSocket-Verbindungen verarbeiten.

Installation

Für die Installation von msw in einem TypeScript-Projekt führen Sie einen der folgenden Befehle aus:

# npm
npm install msw --save-dev

# yarn
yarn add msw --dev

# pnpm
pnpm add msw --save-dev

Nach der Installation muss der Service Worker für die Browser-Nutzung initialisiert werden:

npx msw init public/ --save

Für die serverseitige Nutzung (Node.js) ist keine zusätzliche Initialisierung erforderlich.

Schnellstart – Minimales Arbeitsbeispiel

Erstellen wir einen einfachen Mock für eine REST API. Das Beispiel umfasst die Definition eines Handlers, die Einrichtung des Servers und eine Testanfrage.

// handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('https://api.example.com/user', () => {
    return HttpResponse.json({
      id: 'c7b3d8e0-5e0b-4b0f-8b3a-3b9f4b3d3b3f',
      firstName: 'John',
      lastName: 'Maverick',
    });
  }),
];

// server.ts (für Node.js)
import { setupServer } from 'msw/node';
import { handlers } from './handlers';

export const server = setupServer(...handlers);

// test.ts
import { server } from './server';

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test('should fetch user', async () => {
  const response = await fetch('https://api.example.com/user');
  const data = await response.json();
  expect(data.firstName).toBe('John');
});

Wichtige Methoden und Funktionen

1. http.get()

Signatur: http.get(url: string | RegExp, resolver: (info: HttpRequestInfo) => HttpResponse)

Beschreibung: Erstellt einen Handler für GET-Anfragen an die angegebene URL. Unterstützt exakte Zeichenfolgen, reguläre Ausdrücke und Vorlagen mit Parametern.

import { http, HttpResponse } from 'msw';

export const handler = http.get('https://api.example.com/users/:id', ({ params }) => {
  const { id } = params;
  return HttpResponse.json({ id, name: 'Benutzer ' + id });
});

2. http.post()

Signatur: http.post(url: string | RegExp, resolver: (info: HttpRequestInfo) => HttpResponse)

Beschreibung: Erstellt einen Handler für POST-Anfragen. Ermöglicht das Auslesen des Anforderungstextes über request.json().

import { http, HttpResponse } from 'msw';

export const handler = http.post('https://api.example.com/users', async ({ request }) => {
  const newUser = await request.json();
  return HttpResponse.json({ ...newUser, id: 'generated-id' }, { status: 201 });
});

3. http.put()

Signatur: http.put(url: string | RegExp, resolver: (info: HttpRequestInfo) => HttpResponse)

Beschreibung: Erstellt einen Handler für PUT-Anfragen. Nützlich zum Aktualisieren von Ressourcen.

import { http, HttpResponse } from 'msw';

export const handler = http.put('https://api.example.com/users/:id', async ({ params, request }) => {
  const { id } = params;
  const updates = await request.json();
  return HttpResponse.json({ id, ...updates });
});

4. http.delete()

Signatur: http.delete(url: string | RegExp, resolver: (info: HttpRequestInfo) => HttpResponse)

Beschreibung: Erstellt einen Handler für DELETE-Anfragen. Simuliert das Löschen von Ressourcen.

import { http, HttpResponse } from 'msw';

export const handler = http.delete('https://api.example.com/users/:id', ({ params }) => {
  const { id } = params;
  return HttpResponse.json({ message: `Benutzer ${id} gelöscht` });
});

5. http.all()

Signatur: http.all(url: string | RegExp, resolver: (info: HttpRequestInfo) => HttpResponse)

Beschreibung: Erstellt einen Handler, der auf alle HTTP-Methoden (GET, POST, PUT, DELETE, PATCH usw.) für die angegebene URL reagiert.

import { http, HttpResponse } from 'msw';

export const handler = http.all('https://api.example.com/*', () => {
  return HttpResponse.json({ status: 'Mock aktiv' });
});

6. HttpResponse.json()

Signatur: HttpResponse.json(body: any, init?: ResponseInit)

Beschreibung: Erstellt eine JSON-Antwort. Der zweite Parameter erlaubt die Angabe von Statuscode, Headern usw.

import { HttpResponse } from 'msw';

// Einfache JSON-Antwort
const response1 = HttpResponse.json({ message: 'Erfolg' });

// JSON-Antwort mit Statuscode und Headern
const response2 = HttpResponse.json(
  { error: 'Nicht gefunden' },
  { status: 404, headers: { 'X-Custom-Header': 'Wert' } }
);

7. HttpResponse.text()

Signatur: HttpResponse.text(body: string, init?: ResponseInit)

Beschreibung: Erstellt eine Textantwort. Nützlich für APIs, die einfachen Text oder HTML zurückgeben.

import { HttpResponse } from 'msw';

const response = HttpResponse.text('Hallo Welt!', {
  headers: { 'Content-Type': 'text/plain' }
});

8. HttpResponse.xml()

Signatur: HttpResponse.xml(body: string, init?: ResponseInit)

Beschreibung: Erstellt eine XML-Antwort. Setzt automatisch den Content-Type auf application/xml.

import { HttpResponse } from 'msw';

const xmlData = `BenutzerHallo`;
const response = HttpResponse.xml(xmlData);

9. HttpResponse.arrayBuffer()

Signatur: HttpResponse.arrayBuffer(body: ArrayBuffer, init?: ResponseInit)

Beschreibung: Erstellt eine Antwort mit einem ArrayBuffer. Nützlich zum Mocken von Binärdaten wie Bildern oder Dateien.

import { HttpResponse } from 'msw';

const buffer = new ArrayBuffer(8);
const response = HttpResponse.arrayBuffer(buffer);

10. delay()

Signatur: delay(durationMs: number) oder delay(mode: 'real')

Beschreibung: Fügt eine Verzögerung zur Antwort hinzu, um reale Netzwerklatenz zu simulieren. Nützlich zum Testen von Ladezuständen.

import { http, HttpResponse, delay } from 'msw';

export const handler = http.get('https://api.example.com/slow-endpoint', async () => {
  await delay(2000); // 2 Sekunden Verzögerung
  return HttpResponse.json({ data: 'Verzögerte Antwort' });
});

11. bypass()

Signatur: bypass(request: Request): Promise<Response>

Beschreibung: Leitet die Anfrage an den echten Server weiter, anstatt sie zu mocken. Nützlich, wenn nur bestimmte Endpunkte gemockt werden sollen.

import { http, bypass } from 'msw';

export const handler = http.get('https://api.example.com/external-data', async ({ request }) => {
  // Nur für bestimmte Bedingungen mocken
  if (request.headers.get('x-mock') === 'true') {
    return HttpResponse.json({ mocked: true });
  }
  // Ansonsten an den echten Server weiterleiten
  return bypass(request);
});

12. setupServer()

Signatur: setupServer(...handlers: HttpHandler[]): SetupServerApi

Beschreibung: Erstellt einen Testserver für Node.js-Umgebungen. Akzeptiert eine Liste von Handlern und gibt ein Objekt mit listen(), close(), resetHandlers() und use() zurück.

import { setupServer } from 'msw/node';
import { handlers } from './handlers';

const server = setupServer(...handlers);

// Server starten
server.listen();

// Handler zurücksetzen
server.resetHandlers();

// Server stoppen
server.close();

// Zusätzliche Handler hinzufügen
server.use(
  http.get('https://api.example.com/extra', () => {
    return HttpResponse.json({ extra: true });
  })
);

13. setupWorker()

Signatur: setupWorker(...handlers: HttpHandler[]): SetupWorkerApi

Beschreibung: Erstellt einen Service Worker für die Browser-Umgebung. Muss nach der Initialisierung mit npx msw init verwendet werden.

import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';

const worker = setupWorker(...handlers);

// Worker starten
worker.start({
  onUnhandledRequest: 'bypass', // Unbehandelte Anfragen durchlassen
});

// Worker stoppen
worker.stop();

14. graphql.query() und graphql.mutation()

Signatur: graphql.query(operationName: string, resolver: (info: GraphQLRequestInfo) => HttpResponse)

Beschreibung: Erstellt Handler für GraphQL-Abfragen und -Mutationen. Ermöglicht das Mocken von GraphQL-Endpunkten mit Typensicherheit.

import { graphql, HttpResponse } from 'msw';

export const handlers = [
  graphql.query('GetUser', ({ variables }) => {
    const { id } = variables;
    return HttpResponse.json({
      data: {
        user: { id, name: 'Max Mustermann', email: 'max@example.com' }
      }
    });
  }),

  graphql.mutation('UpdateUser', ({ variables }) => {
    const { id, name } = variables;
    return HttpResponse.json({
      data: {
        updateUser: { id, name, success: true }
      }
    });
  })
];

Praktische Tipps für die Arbeit mit msw und TypeScript

Hier sind einige bewährte Methoden für die effektive Nutzung von msw in TypeScript-Projekten:

Typisierte Handler erstellen

import { http, HttpResponse, StrictRequest } from 'msw';

interface User {
  id: string;
  name: string;
  email: string;
}

// Typisierter Handler mit benutzerdefinierten Typen
export const getUserHandler = http.get<{ id: string }>(
  'https://api.example.com/users/:id',
  ({ params }) => {
    const user: User = {
      id: params.id,
      name: 'Anna Schmidt',
      email: 'anna@example.com'
    };
    return HttpResponse.json(user);
  }
);

Dynamische Daten mit Faker.js

import { http, HttpResponse } from 'msw';
import { faker } from '@faker-js/faker';

export const handlers = [
  http.get('https://api.example.com/users', () => {
    const users = Array.from({ length: 10 }, () => ({
      id: faker.string.uuid(),
      name: faker.person.fullName(),
      email: faker.internet.email(),
      avatar: faker.image.avatar()
    }));
    return HttpResponse.json(users);
  })
];

Fehlerszenarien testen

import { http, HttpResponse } from 'msw';

export const errorHandler = http.get(
  'https://api.example.com/users/error',
  () => {
    return HttpResponse.json(
      { message: 'Interner Serverfehler' },
      { status: 500 }
    );
  }
);

// Im Test: Fehlerbehandlung prüfen
test('sollte 500 Fehler behandeln', async () => {
  server.use(errorHandler);
  
  const response = await fetch('https://api.example.com/users/error');
  expect(response.status).toBe(500);
  
  const data = await response.json();
  expect(data.message).toBe('Interner Serverfehler');
});

Häufige Fehler und Problemlösungen

1. Service Worker wird nicht geladen

Problem: Der Service Worker wird im Browser nicht registriert.

Lösung: Stellen Sie sicher, dass npx msw init public/ --save ausgeführt wurde und die mockServiceWorker.js-Datei im öffentlichen Verzeichnis liegt. Überprüfen Sie auch die Browser-Konsole auf Fehler.

3. TypeScript-Typfehler

Problem: TypeScript meldet Typfehler bei der Verwendung von msw.

Lösung: Stellen Sie sicher, dass die neueste Version von msw installiert ist und Ihre tsconfig.json die ESM-Module unterstützt. Fügen Sie ggf. "moduleResolution": "bundler" oder "esModuleInterop": true hinzu.

4. Anfragen werden nicht abgefangen

Problem: msw fängt bestimmte Anfragen nicht ab.

Lösung: Überprüfen Sie, ob die URL im Handler exakt mit der gesendeten Anfrage übereinstimmt. Verwenden Sie http.all() mit einem Platzhalter, um alle Anfragen zu erfassen, und prüfen Sie die Konsole auf unbehandelte Anfragen.

Fazit

msw ist eine leistungsstarke und flexible Bibliothek zum Mocken von Netzwerkanfragen in TypeScript-Projekten. Durch die Arbeit auf Service Worker-Ebene bietet sie eine einzigartige Kombination aus Realitätsnähe und Einfachheit. Ob für Unit-Tests, Integrationstests oder die Frontend-Entwicklung – msw macht das Testen und Entwickeln von API-abhängigen Anwendungen deutlich effizienter.

Mit der Unterstützung für REST, GraphQL, WebSockets und verschiedenen Antwortformaten (JSON, XML, Text, ArrayBuffer) deckt msw die meisten Anwendungsfälle ab. Die nahtlose Integration mit TypeScript und die Möglichkeit, sowohl im Browser als auch in Node.js zu arbeiten, machen es zu einem unverzichtbaren Werkzeug für moderne Webentwickler.

Empfehlungen