Schema, MSW und MockedProvider richtig einsetzen
Ohne Mocking blockiert ein nicht fertiger Backend-Endpoint die gesamte Frontend-Entwicklung. GraphQL-Mocking auf Schema-Ebene macht Frontend-Teams unabhängig, ermöglicht Fehlerszenario-Tests und beschleunigt den Entwicklungszyklus erheblich.
Inhaltsverzeichnis
- 1. Warum GraphQL-Mocking mehr ist als Dummy-Daten
- 2. Schema-basiertes Mocking: addMocksToSchema
- 3. MSW im Browser: Queries ohne echten Server
- 4. Apollo MockedProvider für Komponententests
- 5. Fehlerszenarien und partielle Fehler mocken
- 6. Mocking für Magento-GraphQL-Endpoints
- 7. Contract-Tests zwischen Mock und echtem Schema
- 8. Mocking-Strategien im Vergleich
- 9. Zusammenfassung
- 10. Das Wichtigste auf einen Blick
- 11. FAQ
1. Warum GraphQL-Mocking mehr ist als Dummy-Daten
Der naheliegendste Ansatz im Frontend: eine JavaScript-Datei mit hartcodierten Testdaten anlegen und in Komponenten-Props einsetzen. Das funktioniert für einzelne Unit-Tests, versagt aber sobald eine Komponente selbst eine GraphQL-Query absetzt, sobald Netzwerk-Ladezeiten simuliert werden sollen oder sobald Fehlerpfade getestet werden müssen. Echter GraphQL-Mocking operiert auf der Ebene des Netzwerk-Handlers oder des GraphQL-Clients und fängt Queries genau so ab, wie es der echte Server täte.
Die Vorteile gehen weit über Isolation hinaus. Ein guter Mock simuliert auch langsame Netzwerke, Authentifizierungsfehler, partielle Datenfehler innerhalb einer Response und Timeout-Verhalten. Wer nur mit statischen Fixture-Dateien arbeitet, bekommt zuverlässige Tests für den Erfolgsfall – aber keine Confidence für die Fälle, die in Produktion am häufigsten zu Supporttickets führen. GraphQL-Mocking auf Schema-Basis stellt außerdem sicher, dass gemockte Daten immer schema-konform sind – ein Mock, der nicht zum Schema passt, scheitert früh im Entwicklungsprozess, nicht in Produktion.
2. Schema-basiertes Mocking: addMocksToSchema
Der Einstieg in schema-basiertes GraphQL-Mocking beginnt mit dem Paket @graphql-tools/mock. Die Funktion addMocksToSchema nimmt ein bestehendes Schema-Objekt und ergänzt jeden Resolver automatisch mit realistischen Standardwerten: Strings werden als leere Strings gemockt, Integers als 42, Booleans als false. Für produktiv einsetzbare Tests überschreibt man diese Defaults mit eigenen Mock-Resolvern pro Typ und Feld. Das Schema selbst wird entweder aus der SDL geladen oder per Introspection vom echten Endpoint bezogen.
Der entscheidende Vorteil dieses Ansatzes: die Mock-Antworten werden durch den vollständigen GraphQL-Execution-Stack verarbeitet. Das bedeutet, dass Validierung, Typprüfung und Feld-Auflösung genauso ablaufen wie beim echten Server. Gibt ein Client eine Query ab, die nicht zum Schema passt, schlägt sie auch gegen den Mock fehl – was Fehler, die sonst erst bei echten API-Calls auftauchen, in die lokale Entwicklungszeit verlagert. Für Teams, die Schema-First arbeiten, ist das der bevorzugte Ansatz: Schema festschreiben, Mock-Server aufsetzen, Frontend-Entwicklung starten – ohne auf Backend-Implementierung zu warten.
# Mock-Schema für einen Produktkatalog (Schema-First-Ansatz)
# Dieses Schema wird von addMocksToSchema genutzt
type Query {
product(sku: String!): Product
products(search: String, pageSize: Int): ProductList!
cart(cartId: String!): Cart
}
type Product {
sku: String!
name: String!
price_range: PriceRange!
stock_status: StockStatus!
description: String
}
type ProductList {
items: [Product!]!
total_count: Int!
}
type PriceRange {
minimum_price: Price!
}
type Price {
final_price: Money!
}
type Money {
value: Float!
currency: String!
}
type Cart {
id: String!
items: [CartItem!]!
prices: CartPrices!
}
type CartItem {
uid: String!
quantity: Int!
product: Product!
}
type CartPrices {
grand_total: Money!
}
enum StockStatus {
IN_STOCK
OUT_OF_STOCK
}
3. MSW im Browser: Queries ohne echten Server
Mock Service Worker (MSW) ist der bevorzugte Ansatz für Browser-basiertes GraphQL-Mocking, weil er auf der Service-Worker-API des Browsers operiert und damit echte Netzwerkanfragen abfängt. Das Frontend-Code-Verhalten ändert sich dabei gar nicht – es sendet echte Fetch-Requests, die MSW interceptiert und mit konfigurierten Responses beantwortet. Im Gegensatz zu Monkey-Patching von fetch oder Axios-Adaptern funktioniert MSW mit jeder HTTP-Bibliothek und auch in Playwright-E2E-Tests.
Für GraphQL-Queries bietet MSW den graphql.query() und graphql.mutation() Handler. Der Handler matcht über den Operation-Namen, nicht über die URL allein – was es einfach macht, mehrere Endpoints mit unterschiedlichen Responses zu konfigurieren. In Entwicklungsumgebungen aktiviert man MSW per Service-Worker-Registration im Browser; in Vitest oder Jest läuft MSW über Node-Interceptors ohne Service-Worker. Derselbe Handler-Code funktioniert damit in allen Testebenen, was Konsistenz zwischen Unit-, Integrations- und E2E-Tests sicherstellt.
# Abgefangene Query: MSW matcht über den Operation-Namen "GetProduct"
# Der Handler in handlers.ts gibt diese Struktur als Response zurück
query GetProduct($sku: String!) {
product(sku: $sku) {
sku
name
price_range {
minimum_price {
final_price {
value
currency
}
}
}
stock_status
description
}
}
# Fehlerfall-Query: gleiche Operation, MSW-Handler gibt errors[] zurück
# Testet die Fehlerbehandlung im Frontend ohne Serveränderung
query GetProductError($sku: String!) {
product(sku: $sku) {
sku
name
}
}
4. Apollo MockedProvider für Komponententests
Wer Apollo Client im React-Frontend einsetzt, bekommt mit MockedProvider eine direkte Mocking-Lösung für Komponenten-Tests. Der MockedProvider ersetzt den echten Apollo-Client in der Test-Rendertree und antwortet auf definierte Queries mit vorbereiteten Antworten. Die Konfiguration erfolgt über ein Array von MockedResponse-Objekten, die jeweils eine Query-Operation und die gewünschte Antwort oder einen Fehler enthalten.
Ein häufiger Fallstrick beim Apollo MockedProvider: Queries müssen exakt übereinstimmen, inklusive Variablen. Stimmt die im Test verwendete Query nicht bitgenau mit der in der Komponente überein – zum Beispiel wegen unterschiedlicher Fragment-Definitionen oder Variablen-Defaults – liefert der MockedProvider keine Antwort und der Test schlägt mit einem Timeout-Fehler fehl. Deshalb empfiehlt sich die Praxis, GraphQL-Documents als .graphql-Dateien zu definieren, die sowohl in Produktionscode als auch in Tests importiert werden. Zusätzlich bietet der addTypename: false-Parameter des MockedProviders eine Vereinfachung für Tests, die keine __typename-Felder benötigen.
5. Fehlerszenarien und partielle Fehler mocken
GraphQL-Fehler haben eine besondere Struktur: Anders als HTTP-Fehler können GraphQL-Responses gleichzeitig Daten und Fehler enthalten. Ein partieller Fehler tritt auf, wenn ein Resolver für ein optionales Feld fehlschlägt, während andere Felder korrekt befüllt werden. Das Frontend muss in diesem Fall entscheiden, ob und wie die partiellen Daten angezeigt werden. Dieses Szenario ist mit statischen Fixture-Daten kaum testbar – ein gutes Mock-Setup ermöglicht es, solche Responses gezielt zu erzeugen.
Mit MSW gibt man im Response-Objekt sowohl data als auch errors zurück, um partielle Fehler zu simulieren. Im Apollo MockedProvider nutzt man das error-Property eines MockedResponse für Netzwerkfehler und das result.errors-Array für GraphQL-Fehler innerhalb einer erfolgreichen HTTP-Response. Wer diese Unterscheidung in Tests nicht abbildet, bekommt keine Aussage darüber, ob das Frontend korrekt auf das Error-Boundary-Verhalten von Apollo reagiert oder ob ein Ladeindikator hängen bleibt, wenn ein Resolver still fehlschlägt.
# Partieller Fehler: cart wird korrekt zurückgegeben,
# aber das Feld "shipping_addresses" schlägt im Resolver fehl.
# MSW-Handler gibt diese Struktur zurück, um Frontend-Verhalten zu testen.
# Erwartete Response-Struktur (JSON):
# {
# "data": {
# "cart": {
# "id": "abc123",
# "items": [...],
# "prices": { "grand_total": { "value": 89.99, "currency": "EUR" } },
# "shipping_addresses": null
# }
# },
# "errors": [
# {
# "message": "Shipping address resolver failed",
# "path": ["cart", "shipping_addresses"],
# "extensions": { "category": "graphql-authorization" }
# }
# ]
# }
query GetCart($cartId: String!) {
cart(cartId: $cartId) {
id
items {
uid
quantity
product { sku name }
}
prices {
grand_total { value currency }
}
shipping_addresses {
city
postcode
}
}
}
6. Mocking für Magento-GraphQL-Endpoints
Bei Magento GraphQL kommen einige spezifische Herausforderungen hinzu, die das Mocking komplexer machen. Magento-Queries laufen gegen einen einzelnen Endpoint (/graphql), nutzen aber store-spezifische Header wie Store: de und optionale Authentifizierungs-Bearer-Tokens für Kundenkontext. MSW-Handler können diese Header auswerten und unterschiedliche Mock-Responses für Gast- und Kundensitzungen liefern. Das ist besonders wertvoll beim Testen von Headless-Frontends, die Warenkorb, Checkout und Kundenkonto über GraphQL abwickeln.
Ein weiterer Magento-spezifischer Aspekt: viele produktive Magento-GraphQL-Queries nutzen Inline-Fragmente auf Union-Typen wie ProductInterface, um zwischen SimpleProduct, ConfigurableProduct und anderen Produkttypen zu unterscheiden. Im Mock muss das __resolveType-Feld korrekt gesetzt werden, damit der GraphQL-Execution-Stack weiß, welchen konkreten Typ er auflösen soll. Schema-basiertes Mocking mit addMocksToSchema löst das automatisch, sofern der Mock-Resolver für ProductInterface einen konkreten Typ zurückgibt.
# Magento-typische Query mit Union-Typ-Inline-Fragmenten
# Muss im Mock __resolveType korrekt zurückgeben
query GetProductDetails($sku: String!) {
products(filter: { sku: { eq: $sku } }) {
items {
__typename
sku
name
price_range {
minimum_price {
final_price { value currency }
}
}
... on ConfigurableProduct {
configurable_options {
attribute_code
label
values { uid label swatch_data { value } }
}
variants {
product { sku name }
attributes { uid label code value_index }
}
}
... on SimpleProduct {
stock_status
only_x_left_in_stock
}
}
}
}
7. Contract-Tests zwischen Mock und echtem Schema
Contract-Tests schließen die gefährliche Lücke zwischen Mocks und Realität: Sie stellen sicher, dass das gemockte Schema mit dem echten Produktions-Schema übereinstimmt. Ohne Contract-Tests können Mocks veralten – eine Mutation bekommt neue Pflichtfelder, ein Typ wird umbenannt, ein Feld erhält einen anderen Rückgabetyp. Der Mock-basierte Komponententest läuft weiter grün, während der Produktionscode bricht.
Das Werkzeug der Wahl ist GraphQL Inspector, das zwei Schemata vergleicht und Breaking Changes identifiziert. Im CI-Prozess lädt ein Job das aktuelle Schema vom Produktions-Endpoint via Introspection und vergleicht es mit dem Schema, auf dem die Mock-Handler basieren. Gibt es Abweichungen, schlägt der CI-Build fehl, bevor der Code in Produktion geht. Für Magento-Projekte empfiehlt sich zusätzlich das Speichern des Schema-Snapshots im Repository, damit auch ohne Netzwerkzugang zum Produktiv-System Schema-Diffs erkannt werden können.
8. Mocking-Strategien im Vergleich
Unterschiedliche Testebenen erfordern unterschiedliche GraphQL-Mocking-Strategien. Die Wahl des richtigen Ansatzes hängt vom Testziel, dem eingesetzten GraphQL-Client und der gewünschten Isolation ab.
| Strategie | Einsatzgebiet | Vorteil | Grenze |
|---|---|---|---|
| addMocksToSchema | Lokaler Mock-Server, Storybook | Schema-validiert, alle Typen automatisch | Kein Netzwerk-Intercepting |
| MSW (graphql.query) | Unit-, Integration-, E2E-Tests | Echter Netzwerk-Stack, client-agnostisch | Kein Schema-Validation per default |
| Apollo MockedProvider | React-Komponenten-Tests | Exakte Query-Kontrolle, Cache-State | Apollo-spezifisch, exaktes Match nötig |
| Fixtures (JSON) | Einfache Unit-Tests, Snapshots | Schnell, kein Setup nötig | Kein Fehlerszenario, veraltet schnell |
| Contract-Tests (Inspector) | CI-Pipeline, Schema-Drift-Erkennung | Verhindert Mock-Realitäts-Drift | Erfordert Schema-Zugriff im CI |
In der Praxis kombiniert man mehrere Strategien: addMocksToSchema für den lokalen Entwicklungsserver und Storybook, MSW für Vitest- und Playwright-Tests und Apollo MockedProvider für isolierte Komponenten-Tests. Die Contract-Tests laufen einmal täglich in der CI-Pipeline und schlagen bei Schema-Drift Alarm, bevor ein neues Frontend-Release deployed wird.
9. Zusammenfassung
Effektives GraphQL-Mocking für Frontend- und API-Tests erfordert mehr als statische Fixture-Dateien. Schema-basiertes Mocking mit addMocksToSchema stellt sicher, dass Mock-Responses immer schema-konform sind. MSW ermöglicht echtes Netzwerk-Intercepting, das in Unit-, Integrations- und E2E-Tests konsistent funktioniert. Apollo MockedProvider gibt volle Kontrolle über Apollo-Cache-State und Query-Matching in Komponenten-Tests. Die Kombination dieser Strategien reduziert fluktuierende Tests, deckt Fehlerpfade ab und macht Frontend-Teams unabhängig von Backend-Entwicklungsstand.
Contract-Tests zwischen Mock-Schema und Produktions-Schema sind der entscheidende Schutz gegen den häufigsten Langzeitfehler: Mocks, die grün bleiben, während das echte Schema längst weiterentwickelt wurde. Mit GraphQL Inspector im CI-Prozess wird dieser Drift automatisch erkannt, bevor er zu Produktionsproblemen wird.
GraphQL Mocking für Frontend- und API-Tests — Das Wichtigste auf einen Blick
Schema-Mocking
addMocksToSchema validiert Mock-Responses gegen das echte Schema – verhindert Mock-Drift in der lokalen Entwicklung und in Storybook.
MSW für alle Testebenen
Mock Service Worker interceptiert echte Netzwerkanfragen, funktioniert client-agnostisch und konsistent in Unit-, Integrations- und E2E-Tests.
Fehlerpfade
Partielle GraphQL-Fehler (data + errors) gezielt mocken – testet Fehlerbehandlung, Ladeindikator-Verhalten und Error-Boundaries im Frontend.
Contract-Tests
GraphQL Inspector im CI vergleicht Mock-Schema mit Produktions-Schema – schlägt Alarm bei Breaking Changes, bevor sie in Produktion gelangen.