Quick answer: Testing GraphQL by saving a collection of hand-written queries in a client GUI proves that those specific requests work today, but it does not prove the client respects the schema, and it gives you no safety when the schema evolves. A schema-driven approach uses the GraphQL schema as the source of truth, validates operations against it, and asserts that the response shape and types match what the operation selected, so a removed field or a changed type fails the build instead of an untested screen. The two are not mutually exclusive; the concrete queries represent real user behavior, while schema validation provides the contract that keeps them honest. If you only keep a GUI collection and run the queries manually, a breaking change can reach production because nothing in the test suite actually compares the operation against the current schema.
GraphQL's flexibility is part of why testing it is easy to do superficially. A client GUI lets you compose a query, send it, see the data come back, and save the request in a folder, which feels like test coverage and is partly that. But GraphQL clients request specific fields, and the failure modes are different from REST: a field can be deprecated and then removed, a nullable field can become non-null or vice versa, and an operation can request something the schema no longer exposes. None of those are caught by re-running a saved query against a live server unless the server happens to reject it, and a happy-path response does not validate the operation document itself. The schema is the artifact that knows all of these rules, which is why durable GraphQL testing is anchored to it rather than to a list of saved requests.
What each style actually guarantees
The collection and the schema-driven test answer different questions.
| Concern | Saved GUI collection | Schema-driven test |
|---|---|---|
| Real operation works | Yes, when run | Yes, via fixtures |
| Operation valid against schema | Not checked | Validated |
| Removed field caught | Only on manual run | Build failure |
| Response type correctness | Not asserted | Type checked |
| Regression in CI | Weak | Strong |
The removed field row is the classic incident: the schema changes, the GUI collection still contains the old query, and because nobody runs every saved request before release, the client sends an invalid operation and the server returns an error.
Validate operations against the schema
Parse and validate the operation documents against the current schema in the test setup, so any operation that requests a field that does not exist or uses the wrong type fails immediately.
Running this validation in CI means the contract is checked against the real schema on every change, which is the core guarantee a static GUI collection cannot provide. The operation files also become readable text that can be reviewed in a pull request rather than buried in a client's internal export format.
Assert the response against the selection
Beyond validating the operation, execute it against a deterministic source or a mocked resolver and assert the returned data matches the selected fields, including null and error cases that the happy path never reaches.
Keeping the saved real-world queries as the fixtures for the schema-driven suite gives you both benefits: the operations represent genuine client behavior, and each is validated against the current contract and run against controlled resolvers. The schema should be treated as a versioned artifact in the repository so the tests always reflect the contract the client will meet.
The practical rule is to keep the schema as the source of truth and validate every operation against it, then assert responses against the selected shape, while using the concrete queries as realistic fixtures. A GUI collection alone is useful for manual exploration and as a record of real requests, but it is not a regression gate because it does not compare operations against the schema. Wire validation into CI and a removed field or changed type becomes a build failure, which is exactly where a breaking change should be caught.
Frequently asked questions
Is a saved collection of GraphQL queries useless?
No. It records real operations and supports manual exploration, but it is not a contract gate. Use those queries as fixtures and validate them against the schema.
How does schema-driven testing catch a removed field?
The operation is parsed and validated against the current schema, so requesting a field that no longer exists produces a validation error and fails the build.
What does a happy-path response fail to prove?
It shows one request returned data, but it does not validate the operation document or check response types and null behavior against the schema.
Should I run GraphQL tests against a live server?
Prefer deterministic mocked resolvers for regression tests, with live smoke tests separate. Live servers share state and cannot reliably exercise error and null branches.
Where should the schema live?
Version it in the repository as a first-class artifact so the test suite always validates operations against the current contract rather than a stale copy.
This is part of the Powerduck vs Hoppscotch comparison — start there for the full picture of API testing choices.
Top comments (0)