Seitennavigationen und Formulare mit MSW 3 mocken
Simulieren Sie Seitenaufrufe und native Formulare mit MSW 3 und http-Handlern. Erfahren Sie mehr über HTML-Antworten, formData und wichtige Änderungen beim Upgrade.
Mock Service Worker (MSW) 3.0.0 ist am 28. September 2026 erschienen und bringt Unterstützung für das Abfangen von Seitennavigationen und Formularübermittlungen (#2721) mit. Ein Klick auf einen Link oder ein einfacher HTML-Formular-Post kann nun also eine gemockte Antwort erhalten, und zwar mit denselben http-Handlern, die Sie bereits schreiben.
Bis zu diesem Release lief ein natives Formular-Submit in einem Storybook- oder Playwright-Setup vollständig an MSW vorbei. Entweder brauchten Sie für genau diesen einen Flow ein echtes Backend, oder Sie ließen den Test weg. In einer Diskussion aus dem Jahr 2023 erklärte der Maintainer, dass MSW Navigations-Requests absichtlich ignoriert, damit nicht versehentlich ein Mock anstelle einer echten HTML-Seite ausgeliefert wird.
Dieser Artikel zeigt, was durch die Änderung testbar wird, welche Breaking Changes in v3 Ihnen als Erstes begegnen und welche Handler-Patterns sich für Seiten und Formular-Posts eignen. Am 10. Oktober 2026 folgte das Patch-Release 3.0.3. Wie Sie MSW installieren und Ihre ersten Handler anlegen, erfahren Sie unter Mocking API servers with Mock Service Worker.
Das Wichtigste in Kürze
- MSW 3.0.0 (28. September 2026) ermöglicht das Abfangen von Seitennavigationen und Formularübermittlungen. Eine eigene API dafür ist jedoch nicht dokumentiert, daher verwenden Sie die Standard-Handler
http.getundhttp.post. - Ein einfaches HTML-Formular sendet seine Felder URL-kodiert und nicht als JSON. Lesen Sie den Body daher mit
request.formData()statt mitrequest.json()aus. - MSW 3 ist ausschließlich als ESM verfügbar und setzt Node.js 22 oder neuer sowie TypeScript 5.9 oder neuer voraus.
onUnhandledRequestheißt jetztonUnhandledFrame, undrequest.headers.get('cookie')liefert innerhalb von Handlern nunnullzurück.
Was ermöglicht das Mocken von Navigationen und Formularen in MSW 3?
Mit MSW 3 können Sie ein serverseitig gerendertes Formular oder einen Link auf eine andere Seite testen, ohne ein Backend zu betreiben. Der Handler liefert das HTML-Dokument der nächsten Seite zurück, und der Browser rendert es genau wie eine echte Serverantwort.
Vor Version 3 deckte MSW nur Requests ab, die Ihr JavaScript per fetch oder XHR absetzte. Außen vor blieben damit etwa ein per Progressive Enhancement umgesetztes Registrierungsformular, ein Suchformular, das die Seite neu lädt, oder ein „Details anzeigen“-Link auf eine serverseitig gerenderte Seite. Jetzt können Sie jeden dieser Fälle als Handler beschreiben und Assertions darauf formulieren, was die Nutzer am Ende tatsächlich sehen.
Am meisten profitiert der Fehlerpfad. Session Replays von Formular-Flows zeigen häufig, dass Nutzer nach dem Absenden auf einer leeren Seite oder einer rohen Fehlerseite landen. Mit einem gemockten POST, der HTML mit Validierungsfehlern oder einen 500er zurückgibt, lässt sich genau dieser Bildschirm noch vor dem Release im Test reproduzieren.
Upgrade auf MSW 3: Die Breaking Changes, die zuerst zuschlagen
Die meisten Test-Suites scheitern zuerst an den Mindestanforderungen an die Laufzeitumgebung und an einer einzigen umbenannten Option. Diese Änderungen aus den Release Notes zu v3.0.0 sollten Sie prüfen, bevor Sie sich den neuen Features widmen:
- MSW ist jetzt ausschließlich als ESM verfügbar (#2765).
- Die minimal unterstützte Node.js-Version ist v22 (#2729).
- TypeScript 5.9 ist nun die Mindestversion.
graphqlwird jetzt alsmsw/graphqlexportiert (#2691), und GraphQL ist nun eine echte optionale Peer Dependency. GraphQL-Handler werden außerdem jetzt übergraphql.link()definiert, dagraphql.query()undgraphql.mutation()aus dem Namespace entfernt wurden.- Die Option
onUnhandledRequestwurde inonUnhandledFrameumbenannt. worker.stop()gibt ein Promise zurück (#2724). Einawaitist nur in kritischen Szenarien nötig, in denen noch Requests unterwegs sind.msw/nativewurde zugunsten von@msw/react-nativeentfernt. Laut einem GitHub-Issue ist@msw/react-nativenoch nicht auf npm veröffentlicht, und der Quellcode zielt weiterhin auf MSW 2. React-Native-Projekte haben daher keinen funktionierenden Upgrade-Pfad, bis das Paket erscheint.
Alles Weitere finden Sie in den Release Notes und im Migrationsleitfaden 2.x → 3.x. Die Code-Snippets in diesem Artikel verwenden den neuen Entrypoint msw/http. Laut der Ankündigung zu MSW 3.0 bleiben die Root-Level-Exporte von msw aus Gründen der Abwärtskompatibilität erhalten. Das nächste Major-Release wird jedoch alles entfernen, was sich über einen eigenen Entrypoint importieren lässt.
Wie mockt man eine Seitennavigation mit http.get?
Um eine Seitennavigation zu mocken, schreiben Sie einen http.get-Handler für den Seitenpfad und geben eine Response mit HTML-Body und dem Content-Type text/html zurück. Die Release Notes zu v3 kündigen zwar das Abfangen von Navigationen und Formularen an, doch weder sie noch der Migrationsleitfaden dokumentieren eine eigene API oder einen speziellen Einrichtungsschritt. Die folgenden Beispiele verwenden daher ausschließlich die Standard-Handler.
Jeder MSW-Request-Handler kombiniert ein Prädikat, das die eingehenden Requests filtert, mit einem Response-Resolver, der die passenden Requests verarbeitet. Laut der Dokumentation zum Abfangen von Requests werden relative Prädikate wie /signup gegen location.href aufgelöst. Gibt ein Resolver eine Response-Instanz zurück, wird diese Instanz zur gemockten Antwort, wie die Dokumentation zur Request-Verarbeitung beschreibt. Deshalb funktioniert hier ein einfaches new Response ganz ohne HTML-spezifischen Helper.
import { http } from 'msw/http'
export const html = (body: string, status = 200) =>
new Response(`<!doctype html><html><body>${body}</body></html>`, {
status,
headers: { 'Content-Type': 'text/html' },
})
export const pageHandlers = [
http.get('/signup', () =>
html(`<form method="post" action="/signup">
<input name="email" type="email">
<button>Sign up</button>
</form>`),
),
]
Suchformulare mit method="get" verhalten sich anders. Ein mit method="get" abgesendetes Formular überträgt seine Felder im Query-String. MSW-Prädikate dürfen keine Query-Parameter enthalten. Matchen Sie daher nur auf den Pfad und lesen Sie die Felder aus new URL(request.url).searchParams aus:
http.get('/search', ({ request }) => {
const q = new URL(request.url).searchParams.get('q') ?? ''
const safe = q.replace(/&/g, '&').replace(/</g, '<')
return html(`<h1>Results for ${safe}</h1>`)
})
Wie mockt man einen Formular-Post mit http.post?
Ein nativer Formular-Post wird von einem http.post-Handler für die action-URL des Formulars verarbeitet. Der Resolver erhält request als standardmäßigen Request der Fetch API, zusammen mit params und cookies. Die Response, die er zurückgibt, wird zur nächsten Seite, die der Browser anzeigt.
Der einzige Unterschied zum bekannten MSW-2-Pattern ist das Format des Bodys. Wenn Ihr eigenes JavaScript ein Formular absendet, schickt es in der Regel JSON:
import { http, HttpResponse } from 'msw/http'
http.post<never, { email: string }>('/api/signup', async ({ request }) => {
const body = await request.clone().json()
return HttpResponse.json({ ok: true, email: body.email }, { status: 201 })
})
Ein einfaches HTML-Formular sendet seine Felder hingegen als URL-kodierte Formulardaten und nicht als JSON, denn application/x-www-form-urlencoded ist die Standardkodierung für Formulare. Ein Aufruf von .json() auf diesem Body schlägt fehl. Lesen Sie ihn stattdessen mit Request.formData() aus. Der MSW-Leitfaden zu Datei-Uploads zeigt, wie formData() auf abgefangene Requests angewendet wird.
import { http } from 'msw/http'
import { html } from './pages'
export const formHandlers = [
http.post('/signup', async ({ request }) => {
const form = await request.clone().formData()
const email = form.get('email')
if (!email) {
return html('<p role="alert">Email is required.</p>', 422)
}
return html('<h1>Check your inbox</h1>')
}),
]
Sehen Sie für jedes Serverergebnis, das Sie testen möchten, einen eigenen Zweig vor: Erfolg, Validierungsfehler und eine 500er-Seite. Ihre Assertions decken dann genau das Dokument ab, das die Nutzer tatsächlich sehen.
Formular-Flows hängen oft von Session-Cookies ab. In v3 liefert request.headers.get('cookie') innerhalb von Handlern keinen gemockten Cookie-String mehr, sondern in allen Umgebungen null. Verwenden Sie für jegliche Cookie-basierte Logik daher das Resolver-Argument cookies.
Wo funktioniert das Abfangen von Navigationen und Formularen in MSW 3?
Die Release Notes zu v3 geben nicht an, welche Umgebungen das Abfangen von Navigationen und Formularen unterstützen. Gleichen Sie PR #2721 und die Dokumentation daher mit Ihrem eigenen Setup ab, bevor Sie sich darauf verlassen. Eine Seitennavigation wird vom Browser ausgeführt, weshalb browserbasierte Test-Runner der naheliegende Ort sind, um diese Handler auszuprobieren.
Die Historie von MSW erklärt, warum. Vor Version 3 ignorierte das Worker-Skript Requests mit dem Modus navigate, wie der Maintainer in Discussion #1578 erläuterte. Wenn Ihre Vitest- oder Jest-Suite Komponententests in Node ausführt, wie in Mocking API calls in Vue tests with Vitest beschrieben, funktionieren die fetch-Mocks weiterhin wie gewohnt. Gehen Sie aber nicht davon aus, dass sich ein natives Formular-Submit dort genauso verhält. Das Release bringt außerdem ein Vite-Plugin mit, msw/vite (#2781), das das Worker-Skript für Sie ausliefert. Sie müssen mockServiceWorker.js also nicht mehr in Ihr Public-Verzeichnis kopieren.
Fazit
Für das Mocken von Seiten und Formularen benötigt MSW 3 kein neues Vokabular. Sie verwenden http.get für die Seite, http.post für das Submit, formData() für den Body und eine Response mit text/html für das Ergebnis. Führen Sie das Upgrade in dieser Reihenfolge durch: Wechseln Sie auf Node.js 22, TypeScript 5.9 und ESM, benennen Sie onUnhandledRequest in onUnhandledFrame um und stellen Sie Ihre Imports auf msw/http um. Ergänzen Sie danach für jedes mögliche Ergebnis Ihres wichtigsten Formulars einen Handler – beginnend mit der Fehlerseite.
FAQs
Funktioniert request.formData() auch bei Formularen mit enctype multipart/form-data?
Ja. Request.formData() parst sowohl Bodys vom Typ application/x-www-form-urlencoded als auch multipart/form-data in ein FormData-Objekt. Derselbe Handler-Code funktioniert also unabhängig davon, ob das Formular ein Datei-Eingabefeld enthält. Textfelder liefert form.get() als Strings zurück, Datei-Eingabefelder als File-Objekte, deren name, size und type Sie auswerten können.
Wie lese ich Checkbox-Gruppen oder Mehrfachauswahlfelder in einem Formular-Handler aus?
Verwenden Sie form.getAll('name') statt form.get('name'). Eine Checkbox-Gruppe oder ein select mit dem Attribut multiple sendet für jeden ausgewählten Wert einen eigenen Eintrag unter demselben Feldnamen, und get() liefert nur den ersten zurück. getAll() gibt ein Array mit allen Werten zurück – oder ein leeres Array, wenn nichts ausgewählt ist, da der Browser nicht angehakte Checkboxen gar nicht erst übermittelt.
Was ist der Unterschied zwischen passthrough() und einem Resolver, der nichts zurückgibt?
Wenn Sie passthrough() zurückgeben, das in MSW 3 aus msw/utils/passthrough importiert wird, geht der Request unverändert an das echte Netzwerk und gilt als verarbeitet, sodass kein nachfolgender Handler ihn mehr beeinflussen kann. Gibt der Resolver nichts zurück, bleibt der Request unangetastet, und MSW versucht es mit dem nächsten passenden Handler. Verwenden Sie passthrough(), wenn eine bestimmte Seite oder Formularübermittlung bewusst einen echten Server erreichen soll.
Muss ich mockServiceWorker.js nach dem Upgrade auf MSW 3 neu generieren?
Ja, es sei denn, Sie verwenden das neue Plugin msw/vite, das das Worker-Skript für Sie ausliefert. Andernfalls führen Sie nach der Installation von MSW 3 npx msw init gefolgt von Ihrem Public-Verzeichnis aus und committen die neue Datei, damit das Worker-Skript zum 3.x-Paket passt und nicht mehr der alten 2.x-Kopie entspricht.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k