mit Mock Schemas als ausführbarem Vertrag
Ein GraphQL-Schema ist bereits ein maschinenlesbarer Vertrag zwischen Frontend und Backend. Mit einem Mock Schema wird dieser Vertrag ausführbar: Frontend-Teams entwickeln gegen realistische Testdaten, lange bevor das Backend fertig ist, und Breaking Changes fallen automatisiert auf, statt erst in Production.
Inhaltsverzeichnis
- 1. Warum Contract Testing bei GraphQL anders funktioniert als bei REST
- 2. Was ein Mock Schema als ausführbarer Vertrag leistet
- 3. Mock Schemas mit @graphql-tools/mock aufsetzen
- 4. Realistische Testdaten: Custom Mock Resolver und Faker
- 5. Frontend-Entwicklung parallel zum Backend
- 6. Konsumenten-Verträge prüfen: welche Felder der Client wirklich nutzt
- 7. Mock Schema und echtes Schema synchron halten
- 8. Standalone Mock-Server: GraphQL Faker und Prism im Vergleich
- 9. Contract-Testing-Strategien im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Contract Testing bei GraphQL anders funktioniert als bei REST
Bei REST-APIs braucht Contract Testing zusätzliche Werkzeuge wie Pact, weil kein maschinenlesbarer Vertrag zwischen Client und Server existiert, der über die Dokumentation hinausgeht. Bei GraphQL ist der Vertrag bereits Teil der Technologie: Das Schema selbst definiert präzise, welche Felder, Typen und Argumente existieren, und jede Query wird gegen genau dieses Schema validiert. Contract Testing für GraphQL APIs muss diesen bereits vorhandenen Vertrag also nicht neu erfinden, sondern nutzbar machen, bevor die eigentliche Implementierung fertig ist.
Der Denkfehler, dem viele Teams unterliegen: Sie verwechseln Schema-Validierung mit Contract Testing. Eine Query, die gegen das Schema valide ist, sagt nichts darüber aus, ob der Server tatsächlich sinnvolle Daten liefert, ob Paginierung korrekt funktioniert, oder ob ein Frontend mit realistischen Antwortformen umgehen kann. Genau diese Lücke schließt Contract Testing mit Mock Schemas: ein ausführbares Schema, das plausible Testdaten für jede Query liefert, ohne dass das echte Backend existieren muss.
2. Was ein Mock Schema als ausführbarer Vertrag leistet
Ein Mock Schema ist ein vollständig ausführbares GraphQL-Schema, bei dem jeder Resolver automatisch generierte oder gezielt konfigurierte Testdaten zurückgibt, statt eine echte Datenquelle anzusprechen. Der entscheidende Unterschied zu einer statischen JSON-Fixture: Ein Mock Schema beantwortet jede beliebige valide Query, inklusive Filtern, verschachtelten Feldern und Variablen, genau wie ein echter Server, nur eben mit generierten statt echten Daten.
Für Contract Testing bedeutet das: Ein Frontend-Team kann gegen das Mock Schema exakt dieselben Queries schreiben, die später gegen das echte Backend laufen, und bekommt strukturell identische Antworten. Ändert sich das Schema später inkompatibel, schlägt dieselbe Query sowohl gegen das Mock Schema als auch gegen das echte Backend fehl, was den Vertrag zwischen beiden Seiten konkret überprüfbar macht, statt ihn nur in einer README zu dokumentieren.
3. Mock Schemas mit @graphql-tools/mock aufsetzen
Das Paket @graphql-tools/mock ist der Standard, um aus einem bestehenden Schema, ob als SDL-Datei oder als Introspection-Ergebnis, automatisch ein vollständig ausführbares Mock Schema zu erzeugen. Die Funktion addMocksToSchema nimmt ein Schema entgegen und generiert für jeden Skalartyp plausible Standardwerte: Strings werden zu Lorem-Ipsum-Text, Booleans alternieren, IDs erhalten eindeutige generierte Werte.
Ohne weitere Konfiguration liefert dieses generische Mocking bereits valide, aber wenig aussagekräftige Antworten. Für ernsthaftes Contract Testing braucht es gezielte Mock-Resolver für die Felder, die tatsächlich fachlich relevant sind, etwa Preise, Lagerbestände oder Bestellstatus, während generische Felder wie interne IDs ruhig zufällig bleiben dürfen.
// mock-server.js — executable mock schema as a contract
const { makeExecutableSchema } = require('@graphql-tools/schema');
const { addMocksToSchema } = require('@graphql-tools/mock');
const { ApolloServer } = require('@apollo/server');
const { startStandaloneServer } = require('@apollo/server/standalone');
const fs = require('fs');
const typeDefs = fs.readFileSync('./schema.graphql', 'utf-8');
const schema = makeExecutableSchema({ typeDefs });
const mockedSchema = addMocksToSchema({
schema,
mocks: {
// Generic scalar mocks apply everywhere unless overridden per field
ID: () => Math.random().toString(36).slice(2, 10),
},
});
const server = new ApolloServer({ schema: mockedSchema });
startStandaloneServer(server, { listen: { port: 4001 } });
4. Realistische Testdaten: Custom Mock Resolver und Faker
Generische Mocks reichen für einen ersten Smoke Test, scheitern aber, sobald ein Frontend fachlich plausible Werte braucht, etwa einen realistischen Preis statt einer zufälligen Fließkommazahl mit zwanzig Nachkommastellen. Der resolvers-Block von addMocksToSchema erlaubt es, für jeden Typ gezielt eine Faker-basierte Generierungsfunktion zu hinterlegen, die auf konkrete Felder zugeschnitten ist.
Ein realistisches Mock Schema für einen Produktkatalog generiert Preise in einem plausiblen Bereich, Produktnamen aus einem Warenkategorie-Vokabular statt aus generischem Lorem Ipsum, und Lagerbestände, die auch mal null sind, damit das Frontend den Fall "nicht vorrätig" testen kann, ohne auf einen echten Datensatz mit diesem Zustand warten zu müssen.
// mock-resolvers.js — domain-realistic data instead of generic lorem ipsum
const { faker } = require('@faker-js/faker');
const mocks = {
Product: () => ({
name: faker.commerce.productName(),
price: Number(faker.commerce.price({ min: 5, max: 500 })),
// ~10% of mocked products are deliberately out of stock
inStock: faker.number.int({ min: 0, max: 10 }) > 0,
}),
Query: () => ({
products: () => Array.from({ length: 12 }, () => ({})),
}),
};
module.exports = { mocks };
5. Frontend-Entwicklung parallel zum Backend
Der praktische Hauptnutzen von Mock Schemas liegt in der Entkopplung von Frontend- und Backend-Fortschritt. Sobald ein Schema als SDL-Entwurf vorliegt, auch wenn noch kein einziger Resolver produktiv implementiert ist, kann das Frontend-Team gegen ein Mock Schema entwickeln, Komponenten bauen und sogar automatisierte Tests schreiben. Das Backend-Team implementiert parallel die echten Resolver gegen dasselbe Schema.
Dieses Modell funktioniert als Contract Testing, weil beide Teams gegen denselben Vertrag arbeiten: das Schema. Divergiert die Backend-Implementierung vom vereinbarten Schema, etwa weil ein Feld doch nullable sein muss, wird das beim nächsten Schema-Sync sofort sichtbar, weit bevor Frontend und Backend zum ersten Mal gegeneinander getestet werden, was in klassischen Integrationstests oft erst kurz vor dem Release passiert.
6. Konsumenten-Verträge prüfen: welche Felder der Client wirklich nutzt
Ein oft übersehener Teil von Contract Testing für GraphQL APIs ist die Frage, welche Felder eines Schemas überhaupt von echten Clients angefragt werden. Ein Backend-Team, das ein Feld entfernen will, muss wissen, ob irgendein Consumer, Web-Frontend, Mobile-App oder Drittanbieter-Integration, dieses Feld tatsächlich verwendet, statt sich auf Vermutungen zu verlassen.
GraphQL Inspector und ähnliche Tools bieten dafür Operation-Coverage-Reports: Alle bekannten Client-Queries werden gesammelt, meist aus den .graphql-Dateien der jeweiligen Frontend-Repositories, und gegen das Schema abgeglichen. Das Ergebnis ist eine Liste ungenutzter Felder, die gefahrlos entfernt werden können, und genutzter Felder, deren Entfernung einen konkreten, benannten Consumer bricht.
7. Mock Schema und echtes Schema synchron halten
Das größte Risiko bei Contract Testing mit Mock Schemas ist Drift: Das Mock Schema wird einmal aus dem echten Schema generiert und danach unabhängig weiterentwickelt, während das echte Backend-Schema sich in eine andere Richtung verändert. Ohne Gegenmaßnahme verliert das Mock Schema nach wenigen Wochen seine Aussagekraft als Vertrag, weil es nicht mehr dasselbe beschreibt wie die Produktion.
Die zuverlässige Lösung ist, das Mock Schema niemals manuell zu pflegen, sondern bei jedem CI-Lauf frisch aus dem aktuellen SDL-Schema oder einer aktuellen Introspection zu generieren. Ergänzend prüft ein Schema-Diff-Tool wie GraphQL Inspector bei jedem Pull Request, ob die vorgenommene Änderung ein Breaking Change ist, und blockiert den Merge, falls ein als genutzt markiertes Feld entfernt oder inkompatibel verändert wird.
# .github/workflows/contract-check.yml
name: GraphQL Contract Check
on: [pull_request]
jobs:
schema-diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
# Compares the new schema against main and fails on breaking changes
- run: npx graphql-inspector diff schema-main.graphql schema.graphql --fail-on-breaking
8. Standalone Mock-Server: GraphQL Faker und Prism im Vergleich
Neben der programmatischen Integration über @graphql-tools/mock gibt es eigenständige Mock-Server, die ohne eigenen Code-Aufwand ein Schema als HTTP-Endpoint bereitstellen. GraphQL Faker startet direkt aus einer SDL-Datei einen laufenden GraphQL-Server mit Faker-basierten Mock-Daten und einem GraphiQL-Explorer, ideal für schnelle Demos oder wenn kein eigenes Node.js-Projekt für den Mock-Server aufgesetzt werden soll.
Prism, ursprünglich für OpenAPI entwickelt, unterstützt inzwischen ebenfalls GraphQL-Mocking und punktet vor allem in Teams, die bereits REST- und GraphQL-APIs parallel betreiben und ein einheitliches Mocking-Tool für beide Vertragstypen bevorzugen. Für reine GraphQL-Projekte bleibt @graphql-tools/mock jedoch die flexiblere Wahl, weil sich Custom-Mock-Resolver direkt in JavaScript oder TypeScript definieren lassen, statt über eine separate Konfigurationssprache.
9. Contract-Testing-Strategien im Vergleich
Je nach Teamgröße und Reifegrad des Schemas eignen sich unterschiedliche Strategien für Contract Testing bei GraphQL APIs.
| Strategie | Nutzt | Stärke | Einschränkung |
|---|---|---|---|
| Generisches Mocking | addMocksToSchema ohne Custom Resolver | In Minuten startklar | Daten fachlich wenig aussagekräftig |
| Faker-basiertes Mocking | Custom Resolver + @faker-js/faker | Realistische, testbare Randfälle | Pflegeaufwand pro Domänentyp |
| Operation-Coverage-Analyse | GraphQL Inspector gegen Client-Queries | Zeigt tatsächlich genutzte Felder | Benötigt Zugriff auf alle Consumer-Repos |
| Schema-Diff in CI | graphql-inspector diff --fail-on-breaking | Verhindert Breaking Changes vor Merge | Erkennt keine semantischen Datenfehler |
In der Praxis kombinieren reife GraphQL-Teams alle vier Strategien: generisches Mocking für schnelle Prototypen, Faker-basiertes Mocking für ernsthafte Frontend-Entwicklung, Operation-Coverage für sichere Deprecations und Schema-Diff in CI als letzte, automatisierte Verteidigungslinie gegen unbeabsichtigte Breaking Changes.
Mironsoft
GraphQL-Contract-Testing, Mock Schemas und CI-Pipelines
Frontend und Backend parallel entwickeln, ohne Überraschungen?
Wir bauen Mock Schemas mit realistischen Testdaten, richten Operation-Coverage-Reports ein und integrieren Schema-Diff-Checks in eure CI-Pipeline, damit Breaking Changes vor dem Merge auffallen, nicht danach.
Mock-Schema-Setup
addMocksToSchema mit domänenspezifischen Faker-Resolvern konfigurieren
Coverage-Reports
Genutzte Felder über alle Consumer-Repositories hinweg sichtbar machen
CI-Schema-Diff
Breaking-Change-Erkennung als verbindliches Pull-Request-Gate
10. Zusammenfassung
Contract Testing für GraphQL APIs mit Mock Schemas nutzt die Tatsache, dass GraphQL bereits einen maschinenlesbaren Vertrag mitbringt, das Schema selbst. Ein mit addMocksToSchema und Faker-basierten Resolvern angereichertes Mock Schema macht diesen Vertrag ausführbar, lange bevor die echte Implementierung steht, und erlaubt Frontend- und Backend-Teams, unabhängig voneinander gegen denselben Vertrag zu arbeiten.
Der Vertrag bleibt nur so lange verlässlich, wie er automatisiert geprüft wird: Operation-Coverage-Reports zeigen, welche Felder echte Consumer nutzen, und Schema-Diff-Checks in der CI-Pipeline verhindern, dass Breaking Changes unbemerkt den main-Branch erreichen. Zusammen ersetzen diese Bausteine aufwendige, manuell koordinierte Integrationstests durch automatisiertes, jederzeit wiederholbares Contract Testing.
Contract Testing für GraphQL APIs — Das Wichtigste auf einen Blick
Schema als Vertrag
GraphQL bringt den Vertrag bereits mit, das Schema selbst. Mock Schemas machen ihn ausführbar.
Realistische Testdaten
addMocksToSchema plus Faker-Resolver statt generischem Lorem Ipsum für aussagekräftige Tests.
Coverage statt Vermutung
Operation-Coverage-Reports zeigen, welche Felder echte Consumer tatsächlich anfragen.
Automatisierte Absicherung
Schema-Diff-Checks in CI verhindern Breaking Changes vor dem Merge, nicht danach.