22GraphQL & gRPC Testing
What you will master here
- GraphQL: queries, mutations, subscriptions, fragments
- Testing GraphQL endpoints (HTTP POST → JSON)
- Schema introspection + breaking-change detection
- gRPC: Protobuf, unary vs streaming RPCs
- Testing gRPC services (grpcurl, ghz, grpc-node)
- When each is used vs REST
22.1 GraphQL essentials
- Single endpoint — usually
/graphql. Always POST, body = JSON withquery+variables. - Client asks for exactly the fields it needs — no over/under fetching.
- 3 operation types: query (read), mutation (write), subscription (WebSocket stream).
- Schema — strongly typed; introspectable.
// Request
POST /graphql
Content-Type: application/json
{
"query": "query GetUser($id: ID!) { user(id: $id) { id name email orders { id total } } }",
"variables": { "id": "u_123" }
}
// Response
{
"data": {
"user": {
"id": "u_123",
"name": "Alice",
"email": "alice@test.com",
"orders": [{ "id": "o_1", "total": 99.0 }]
}
}
}
22.2 Testing GraphQL with Playwright
test('GraphQL — fetch user with orders', async ({ request }) => {
const r = await request.post('/graphql', {
data: {
query: `query($id: ID!){ user(id: $id){ id name orders { id total } } }`,
variables: { id: 'u_123' },
},
headers: { Authorization: `Bearer ${token}` },
});
expect(r.status()).toBe(200);
const body = await r.json();
expect(body.errors).toBeUndefined();
expect(body.data.user.email).toBe('alice@test.com');
expect(body.data.user.orders.length).toBeGreaterThan(0);
});
GraphQL pitfallEven errors come back with HTTP 200! Check
body.errors, not status. A query with a typo returns 200 + errors: [{...}].22.3 GraphQL test patterns
- Field selection asymmetry — ask for a small subset to confirm a field works; ask for everything to detect new required fields the server forces.
- Mutation + immediate read — POST a mutation, then query the resource and verify state.
- Variable validation — pass invalid types/missing variables to confirm schema validation returns errors.
- Persisted queries — production GraphQL APIs often only allow whitelisted query hashes for security. Tests need to send the hash + params.
- Subscriptions — open WebSocket, subscribe, trigger event via another API, assert payload.
22.4 Introspection + breaking-change detection
// Introspection query — fetches the whole schema
{ "query": "{ __schema { types { name fields { name type { name } } } } }" }
// Production tools to diff schema between releases:
npx graphql-inspector diff schema-old.graphql schema-new.graphql
// Flags removed fields, type changes — gates breaking changes in CI
22.5 gRPC essentials
- Binary protocol over HTTP/2; payloads are Protobuf (efficient binary).
- 4 RPC types: unary (req → res), server streaming, client streaming, bidirectional streaming.
- Schema =
.protofile; both client + server generate code from it. - Used heavily in microservices (Google, internal services).
// users.proto
syntax = "proto3";
service Users {
rpc GetUser(GetUserRequest) returns (User);
rpc ListUsers(ListUsersRequest) returns (stream User); // server-streaming
}
message GetUserRequest { string id = 1; }
message User { string id = 1; string email = 2; }
22.6 Testing gRPC — tools
grpcurl — CLI equivalent of curl
grpcurl -plaintext -d '{"id": "u_123"}' \
localhost:50051 users.Users/GetUser
# List methods on a server with reflection enabled
grpcurl -plaintext localhost:50051 list users.Users
Node test via grpc-js
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
const def = protoLoader.loadSync('users.proto');
const proto: any = grpc.loadPackageDefinition(def).users;
const client = new proto.Users('localhost:50051', grpc.credentials.createInsecure());
test('gRPC GetUser', (done) => {
client.GetUser({ id: 'u_123' }, (err: any, res: any) => {
expect(err).toBeNull();
expect(res.email).toBe('alice@test.com');
done();
});
});
ghz — gRPC load tester
ghz --insecure --proto users.proto --call users.Users/GetUser \
-d '{"id":"u_123"}' -c 50 -n 10000 localhost:50051
22.7 When each is used
| API style | Best for | Tooling |
|---|---|---|
| REST | Public APIs, broad client compat | Postman / Playwright request / REST Assured |
| GraphQL | UI-heavy apps; many fields, varied client needs | Same HTTP tools; graphql-inspector for schema |
| gRPC | Internal microservices, performance-critical | grpcurl, ghz, language-specific clients |
| WebSocket / SSE | Real-time push | Playwright supports WebSocket events; specialised libs for SSE |
Module 48 — GraphQL/gRPC Q&A
Why does a failed GraphQL query return HTTP 200?
GraphQL treats errors as application-level data — they ride in
body.errors alongside any partial data. The HTTP layer just signals "the GraphQL endpoint received and processed your request". Always inspect body.errors instead of status.How is GraphQL different from REST in testing strategy?
REST: many endpoints, each with own contract. GraphQL: one endpoint, schema-driven. Tests pivot from URL-based to query-based. Schema introspection enables automated breaking-change detection at the contract layer.
What's a mutation in GraphQL?
An operation that changes server state — analogous to POST/PUT/DELETE. Syntax:
mutation { createUser(input: {...}) { id } }. Same HTTP shape as a query (POST /graphql) but the server runs the mutation resolvers.How do you test a GraphQL subscription?
Open a WebSocket to the GraphQL endpoint, send a subscription operation, then trigger the event via a separate mutation. Assert the streamed payload matches. Tools: graphql-ws client, or Apollo Client's test utilities.
What is gRPC?
A binary RPC framework over HTTP/2 with Protobuf payloads. Strongly typed; clients + servers generated from .proto files. Lower latency and smaller payloads than JSON over REST, ideal for high-throughput microservices.
What's the difference between unary and streaming RPC?
Unary: one request → one response (like HTTP). Server-streaming: one request → stream of responses. Client-streaming: stream of requests → one response. Bidirectional: both stream. Used for chat, telemetry, file uploads.
What's grpcurl?
CLI tool for invoking gRPC methods — like curl for REST. Supports reflection so you can list services without a .proto file. Used for ad-hoc testing and CI smoke tests.
How do you load-test a gRPC service?
Use ghz — a Go-based gRPC load tester. Configure RPS, duration, payload, then it produces a report with latency percentiles. Equivalent of k6 for gRPC.
What's GraphQL introspection?
A query that returns the schema itself — types, fields, arguments. Clients and tools use it for auto-completion (GraphiQL) and contract checks. Often disabled in production for security.