Use when GraphQL subscriptions need tests over graphql-ws (WebSocket) or graphql-sse: subscribing through the async iterator, asserting event shape and order, connection lifecycle and close codes, auth on connect and pubsub triggers. Queries and mutations belong to the server test skills.
90%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Isolate the resolver's pubsub wiring from the transport stack, then assert the filter predicate and error propagation.
Isolate the resolver's pubsub wiring without a full transport stack using
the graphql subscribe function directly:
import { subscribe, parse } from 'graphql';
import { schema, pubsub } from './schema';
it('resolver emits events published to the channel', async () => {
const result = await subscribe({
schema,
document: parse('subscription { messageAdded { id text } }'),
});
if ('errors' in result) throw new Error('Subscription failed');
// Publish after subscribing
pubsub.publish('MESSAGE_ADDED', { messageAdded: { id: '1', text: 'hello' } });
const { value } = await result.next();
expect(value.data).toEqual({ messageAdded: { id: '1', text: 'hello' } });
await result.return?.(); // clean up iterator
});This tests the resolver in isolation - no WebSocket server, no client library. Pair with transport-layer tests for full coverage.
it('only emits events that pass the filter predicate', async () => {
const sub = client.iterate({
query: 'subscription Messages($channel: String!) { messageAdded(channel: $channel) { text } }',
variables: { channel: 'team-a' },
});
pubsub.publish('MESSAGE_ADDED', { channel: 'team-b', messageAdded: { text: 'wrong' } });
pubsub.publish('MESSAGE_ADDED', { channel: 'team-a', messageAdded: { text: 'right' } });
const { value } = await sub.next();
expect(value.data?.messageAdded?.text).toBe('right');
await sub.return?.();
});Per the-guild.dev/graphql/sse/recipes, resolver errors during a subscription should surface via the iterator, not crash the server:
it('surfaces resolver errors as GraphQL errors, not exceptions', async () => {
const result = await subscribe({
schema: errorSchema, // schema whose subscription resolver throws
document: parse('subscription { failingFeed }'),
});
if ('errors' in result) throw new Error('Subscribe itself failed');
const { value } = await result.next();
expect(value.errors).toBeDefined();
expect(value.errors?.[0].message).toMatch(/expected error/i);
});