PHPUnit, Integrationstests und echte Requests
Ungetestete GraphQL-Resolver sind eine Zeitbombe: Schema-Änderungen brechen Frontend-Verträge unbemerkt, Authentifizierungsfehler bleiben bis zum produktiven Einsatz verborgen. Dieser Artikel zeigt, wie man Magento GraphQL auf drei Ebenen testet – von Unit-Tests für Resolver-Logik bis zu echten HTTP-Requests gegen den Endpoint.
Inhaltsverzeichnis
- 1. Teststrategie: drei Ebenen für GraphQL-Resolver
- 2. Unit-Tests: Resolver-Logik ohne Magento-Framework
- 3. Integrationstests mit graphQlQuery()
- 4. Test-Fixtures: Testdaten sauber anlegen
- 5. Authentifizierte Queries testen
- 6. Fehlerszenarien und Exception-Typen testen
- 7. Testarten im Vergleich
- 8. Echte HTTP-Requests: curl und Postman
- 9. Contract-Tests: Frontend-Verträge sichern
- 10. Zusammenfassung
- 11. FAQ
1. Teststrategie: drei Ebenen für GraphQL-Resolver
GraphQL-Resolver in Magento können auf drei Ebenen getestet werden, die sich in Geschwindigkeit, Isolation und Aussagekraft unterscheiden. Unit-Tests sind schnell und isoliert, testen aber nur die interne Logik eines Resolvers ohne echten Datenbankzugriff. Integrationstests laufen im Magento-Framework-Kontext mit einer echten Testdatenbank und sind realistischer, aber deutlich langsamer. HTTP-Tests gegen den echten GraphQL-Endpoint sind am nächsten am Produktivbetrieb, erfordern aber eine laufende Magento-Instanz.
Eine sinnvolle Teststrategie kombiniert alle drei Ebenen: Unit-Tests für die Resolver-Logik und Service-Schicht, Integrationstests für die Verbindung zwischen Resolver, Repository und Schema, und mindestens ein HTTP-Test pro Query und Mutation als Smoke-Test im Deployment-Prozess. Wer nur eine Ebene testet – meistens fehlen Integrationstests – hat eine Lücke, die Magento-spezifische Fehler wie falsche di.xml-Konfigurationen oder Schema-Merge-Konflikte nicht aufdeckt.
2. Unit-Tests: Resolver-Logik ohne Magento-Framework
Magento-Resolver sollten so aufgebaut sein, dass ihre Logik ohne den gesamten Magento-Framework-Bootstrap testbar ist. Das bedeutet: Geschäftslogik in Services auslagern, die keine Magento-spezifischen Abhängigkeiten haben, und Resolver als dünne Delegationsschicht halten. Dann kann man den Service mit PHPUnit und einfachen Mock-Objekten testen, ohne eine Magento-Instanz zu starten.
In der Praxis scheitert das oft daran, dass Resolver direkt Repositories oder das ObjectManager-Muster verwenden, die schwer zu mocken sind. Der Schlüssel ist, Abhängigkeiten über den Konstruktor zu injizieren und Interfaces statt konkreter Klassen als Typen zu verwenden. Eine Resolver-Klasse, die ein CustomerBadgeServiceInterface injiziert bekommt, kann in Tests mit einer Mock-Implementierung dieses Interfaces getestet werden – schnell, präzise und ohne Framework-Overhead.
# The query under test — used in both integration tests and HTTP smoke tests
query CustomerBadgeTest {
mironCustomerBadge(customer_id: 1) {
badge_level
points
next_level_threshold
expires_at
}
}
# Mutation test example
mutation UpdateBadgeLevelTest {
mironUpdateBadge(input: {
customer_id: 1
badge_level: "gold"
}) {
success
message
updated_badge {
badge_level
points
}
}
}
3. Integrationstests mit graphQlQuery()
Magento stellt für GraphQL-Integrationstests eine Basisklasse Magento\TestFramework\TestCase\GraphQlAbstract zur Verfügung, die die Methode graphQlQuery() und graphQlMutation() bereitstellt. Diese Methoden senden echte GraphQL-Requests gegen die Testinstanz, laden die Response und geben sie als PHP-Array zurück. Das ermöglicht präzise Assertions auf die exakte Response-Struktur, ohne curl-Commands schreiben zu müssen.
Integrationstests dieser Art laufen in der Testdatenbank von Magento, die für jeden Test-Run fresh aufgesetzt oder über Fixtures mit Daten befüllt wird. Sie sind deutlich langsamer als Unit-Tests – ein einzelner Integrationstest dauert oft 2–10 Sekunden –, aber sie prüfen genau das, was in der Praxis schiefgehen kann: falsche FQCN in di.xml, Schema-Merge-Konflikte, fehlende Berechtigungen und inkorrekte Response-Strukturen. Für jeden neuen Resolver sollte mindestens ein Integrationstest existieren, der den Happy-Path und einen Fehlerfall abdeckt.
# Integration test query — used with Magento's graphQlQuery() method
# Tests the full stack: schema → resolver → service → repository → database
query IntegrationTestProducts {
products(
filter: { sku: { eq: "test-product-001" } }
pageSize: 1
) {
total_count
items {
sku
name
price_range {
minimum_price {
final_price { value currency }
}
}
}
}
}
# Assert in PHPUnit:
# self::assertEquals(1, $response['products']['total_count'])
# self::assertEquals('test-product-001', $response['products']['items'][0]['sku'])
# self::assertNotEmpty($response['products']['items'][0]['price_range'])
4. Test-Fixtures: Testdaten sauber anlegen
Integrationstests brauchen konsistente Testdaten. Magento stellt dafür das Fixture-System mit @magentoDataFixture-Annotationen bereit. Eine Fixture-PHP-Datei legt Produkte, Kategorien, Kunden oder andere Entitäten in der Testdatenbank an und wird nach dem Test automatisch zurückgerollt. Das Magento-Framework nutzt Database-Rollback-Transaktionen, um die Testdatenbank nach jedem Test in den Ausgangszustand zurückzusetzen.
Für GraphQL-Resolver-Tests sind besonders Produkt- und Kunden-Fixtures wichtig. Produkt-Fixtures sollten alle Attribute setzen, die der Resolver zurückgibt – sonst liefern Tests positive Ergebnisse, weil null-Werte mit dem erwarteten Ergebnis übereinstimmen. Kunden-Fixtures müssen reale Kunden-Entitäten anlegen, nicht nur IDs, weil der Magento-Authentifizierungsstack den kompletten Kunden-Record benötigt. Gut geschriebene Fixtures sind deklarativ und kommentiert – sie beschreiben, welche Ausgangslage sie erzeugen, nicht wie sie es technisch tun.
5. Authentifizierte Queries testen
Queries und Mutationen, die einen authentifizierten Kunden erfordern, müssen mit einem Bearer-Token getestet werden. In Integrationstests stellt Magento die Methode graphQlQuery($query, [], '', ['Authorization' => 'Bearer ' . $token]) zur Verfügung. Den Token erhält man über eine Mutation generateCustomerToken oder direkt über den Token-Service. Für Integrationstests, die einen Kunden benötigen, ist die Kombination aus Kunden-Fixture und Token-Generierung der Standardweg.
Besonders wichtig ist es, auch den negativen Fall zu testen: Was passiert, wenn eine Query, die einen authentifizierten Kontext erfordert, ohne Token aufgerufen wird? Der Resolver sollte mit einer GraphQlAuthorizationException antworten, die sich im errors-Array der Response zeigt. Dieser Test prüft, ob die Sicherheitsprüfung tatsächlich implementiert ist – und nicht nur die positive Variante, was ein häufiger Test-Blindspot ist.
6. Fehlerszenarien und Exception-Typen testen
Fehlerszenarien zu testen ist mindestens genauso wichtig wie den Happy-Path zu testen. Für GraphQL-Resolver gibt es drei typische Fehlerklassen, die jeweils explizit getestet werden sollten: Eingabefehler (ungültige Argumente, fehlende Pflichtparameter), nicht gefundene Entitäten (Produkt-ID existiert nicht) und Autorisierungsfehler (Zugriff ohne Token oder mit falschem Scope). Jeder dieser Fälle sollte zu einer spezifischen Exception-Klasse führen, die Magento in eine strukturierte GraphQL-Fehlermeldung umwandelt.
In Integrationstests fängt man diese Fehler über die expectException()-Methode von PHPUnit ab oder prüft das errors-Array der Response, wenn der Test die Response trotz Fehler weiterverarbeiten soll. Letzteres ist sinnvoll, wenn man die genaue Fehlermeldung und Kategorie testen möchte: self::assertEquals('graphql-authorization', $response['errors'][0]['category']). Diese Präzision stellt sicher, dass Frontends auf definierte Fehlerstruktur vertrauen können.
7. Testarten im Vergleich
Jede Testart hat ihre spezifische Rolle in der GraphQL-Teststrategie. Die Auswahl der richtigen Testart für den richtigen Zweck verhindert sowohl übermäßig lange Test-Runs als auch Testlücken.
| Testart | Geschwindigkeit | Was wird geprüft | Was wird nicht geprüft |
|---|---|---|---|
| Unit-Test (PHPUnit) | < 100 ms | Resolver-Logik, Service-Methoden, Datenvalidierung | Schema, di.xml, Datenbankabfragen |
| Magento-Integrationstest | 2–10 s | Schema, Resolver, Repository, Authentifizierung | HTTP-Stack, Varnish-Caching, Lastverhalten |
| HTTP-Smoke-Test (curl) | 0.5–2 s | Endpoint-Verfügbarkeit, HTTP-Stack, Response-Format | Edge-Cases, Datenkorrektheit, Fehlerszenarien |
| Contract-Test | variabel | Schema-Kompatibilität mit Frontend-Queries | Datenbankinhalt, Performance, Authentifizierung |
| Load-Test (k6, JMeter) | Minuten | Performance unter Last, Caching-Verhalten | Fachliche Korrektheit, Error-Cases |
8. Echte HTTP-Requests: curl und Postman
Für schnelle manuelle Tests und als Deployment-Smoke-Test eignen sich echte HTTP-Requests mit curl oder Postman. Ein einfacher curl-Aufruf gegen den GraphQL-Endpoint zeigt sofort, ob der Endpoint erreichbar ist, ob das Schema die eigene Query kennt und ob die Response die erwartete Struktur hat. Dieser Test kann direkt im Deployment-Skript als letzter Schritt eingebaut werden und verhindert, dass eine fehlerhafte Konfiguration unbemerkt in die Produktion gelangt.
Postman ermöglicht darüber hinaus das Speichern und Versionieren von Query-Sammlungen, die das gesamte GraphQL-Modul dokumentieren und gleichzeitig als manuelle Testbasis dienen. In Kombination mit Postman-Environments kann dieselbe Query-Sammlung gegen Entwicklung, Staging und Produktion ausgeführt werden. Für Teams ist das eine pragmatische Methode, GraphQL-Dokumentation und Tests zu kombinieren, ohne ein separates Tool einführen zu müssen.
# HTTP smoke test queries — run these with curl after every deployment
# 1. Verify endpoint is up and schema is valid
# curl -X POST https://shop.example.com/graphql \
# -H 'Content-Type: application/json' \
# -d '{"query":"{ __typename }"}'
# 2. Verify custom query is registered
query SmokeTestCustomQuery {
mironCustomerBadge(customer_id: 1) {
badge_level
points
}
}
# 3. Verify authenticated query with Bearer token
# curl -X POST https://shop.example.com/graphql \
# -H 'Content-Type: application/json' \
# -H 'Authorization: Bearer <customer-token>' \
# -d '{"query":"{ customer { firstname email } }"}'
# 4. Verify error response structure for unauthorized access
query VerifyAuthError {
customer {
firstname
}
}
# Expected: errors[0].category = "graphql-authorization"
9. Contract-Tests: Frontend-Verträge sichern
Contract-Tests prüfen, ob das aktuelle GraphQL-Schema noch mit den Queries kompatibel ist, die das Frontend verwendet. Das Grundprinzip: die Frontend-Queries werden als GraphQL-Dokumente gespeichert und regelmäßig gegen das aktuelle Schema validiert. Wenn eine Schema-Änderung ein Feld entfernt oder umbenennt, das eine Frontend-Query verwendet, schlägt der Contract-Test fehl – bevor die Änderung deployed wird.
GraphQL Inspector ist ein praktisches CLI-Tool für diesen Zweck: graphql-inspector validate schema.graphql queries/*.graphql validiert alle gespeicherten Queries gegen das aktuelle Schema. In einer CI/CD-Pipeline, die nach jedem Merge ausgeführt wird, verhindert das unbeabsichtigte Breaking Changes. Für Magento-Projekte mit mehreren Frontends – etwa ein PWA-Frontend und eine mobile App – ist diese Praxis besonders wertvoll, weil Schema-Änderungen sonst beide Clients gleichzeitig brechen können.
10. Zusammenfassung
Eine vollständige Teststrategie für Magento GraphQL kombiniert Unit-Tests für Resolver-Logik und Services, Integrationstests mit graphQlQuery() für den vollständigen Stack, HTTP-Smoke-Tests für Deployment-Verifikation und Contract-Tests für Schema-Kompatibilität. Jede Ebene hat ihre spezifische Stärke und deckt Fehler ab, die die anderen Ebenen nicht sehen.
Der häufigste Fehler in Magento-Projekten: es gibt Unit-Tests für Services, aber keine Integrationstests für Resolver. Dadurch bleiben Konfigurationsfehler in di.xml, falsche FQCN-Angaben und Schema-Merge-Konflikte unentdeckt. Ein einziger Integrationstest pro Resolver – Happy-Path und ein Fehlerfall – erhöht das Vertrauen in Deployments erheblich und macht Refactoring sicherer.
Magento GraphQL Testing — Das Wichtigste auf einen Blick
Unit-Tests
Resolver-Logik und Services isoliert testen. Abhängigkeiten über Interfaces injizieren, damit Mocks möglich sind. Schnell und präzise für Logikfehler.
Integrationstests
GraphQlAbstract und graphQlQuery() für echte Stack-Tests mit Testdatenbank. Pflicht für jeden neuen Resolver – mindestens Happy-Path + Fehlerfall.
HTTP-Smoke-Tests
curl oder Postman nach jedem Deployment. Prüfen Endpoint-Verfügbarkeit, Schema-Validität und Response-Format im laufenden Betrieb.
Contract-Tests
GraphQL Inspector validiert Frontend-Queries gegen aktuelles Schema. In CI/CD-Pipeline: verhindert Breaking Changes bevor sie deployed werden.
11. FAQ: Magento GraphQL testen
1Welche Basisklasse für GraphQL-Integrationstests?
Magento\TestFramework\TestCase\GraphQlAbstract – stellt graphQlQuery() und graphQlMutation() bereit, die echte Requests senden und Response als PHP-Array zurückgeben.2Resolver ohne Magento-Datenbank testen?
3Was ist @magentoDataFixture?
4Wie teste ich eine Query mit Bearer-Token?
graphQlQuery($query, [], '', ['Authorization' => 'Bearer ' . $token]). Token über generateCustomerToken-Mutation oder Token-Service im Test erzeugen.5Fehlenden Token korrekt testen?
errors[0].category auf graphql-authorization prüfen. Oder expectException(GraphQlAuthorizationException::class).6Was ist ein GraphQL Contract-Test?
7Wie Integrationstests ausführen?
bin/magento dev:tests:run integration oder vendor/bin/phpunit -c dev/tests/integration/phpunit.xml. Separate Testdatenbank muss konfiguriert sein.