本文へ移動
cccskills
無料GitHub で公開

graphql-subscription-test-author

Authors GraphQL subscription resolver test suites over graphql-ws (WebSocket) and graphql-sse (Server-Sent Events) transports: subscribe to event streams via the async-iterator API, assert emitted data shape and sequence, verify connection lifecycle and protocol close codes, and validate auth-on-connect (connectionParams / authenticate callback) plus resolver-level pubsub trigger logic. Use for real-time subscription operations; not for queries or mutations - for those use apollo-server-tests or graphql-yoga-tests (Mercurius in its references).

日本語の概要は準備中です。原文の説明を表示しています。

testland/qaスター 13(GitHub 13 + cccskills 0)
連携ツールCloudflare

実行スコア 未検証

インストール方法を見る

含まれるファイル(6)

  • SKILL.md11.4 KB
  • .tessl-plugin/plugin.json698 B
  • references/auth-on-connect.md2.7 KB
  • references/lifecycle-and-close-codes.md1.3 KB
  • references/resolver-and-filter-tests.md2.3 KB
  • references/transport-server-setup.md1.3 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

graphql-subscription-test-author

Overview

Per the GraphQL October 2021 spec (section 6.3, blocked by Cloudflare Turnstile - cite by stable ID "GraphQL October 2021 spec, Section 6.3: Subscription"), a subscription operation must: select a single root field, return an event stream, and emit one result per event. Each emitted result is executed independently against the schema, exactly like a query.

This skill covers testing the transport and resolver layers for subscriptions. The two most common Node.js transports are:

Both expose an identical async-iterator surface via client.iterate(), making the same test patterns portable across transports.

When to use

  • Writing tests for subscription resolvers (pubsub trigger, filter, error).
  • Asserting connection lifecycle: handshake, auth rejection, protocol close.
  • Verifying auth-on-connect: connectionParams (WS) or authenticate callback (SSE) reject unauthenticated clients before any event is sent.
  • Integration-testing that event-stream shape matches the schema contract.

Distinct scope vs. sibling skills:

  • apollo-server-tests covers queries/mutations via executeOperation; its Limitations section explicitly notes "Doesn't test subscriptions over WS."
  • graphql-yoga-tests covers Yoga's yoga.fetch() path; subscription tests there go through Yoga's own plugin hooks, not graphql-ws/sse directly.
  • graphql-yoga-tests (references/mercurius.md) and hasura-tests target those specific runtimes.

How to use

  1. Install the transport client(s) plus the test runner (graphql-ws + ws, or graphql-sse, alongside graphql and jest).
  2. Start the server on port: 0 in beforeAll so parallel suites each get a free OS-assigned port; dispose() the runner and close() the server in afterAll.
  3. Create a client with createClient and drive the subscription through client.iterate(), pushing each event.data into an array; break once the expected count arrives to close the stream.
  4. Assert the emitted sequence and shape against the schema contract, checking errors is undefined before reading data.
  5. Add auth-on-connect tests: reject a missing / invalid token (close code 4403 for WS, 401 for SSE) and accept a valid one.
  6. Add a resolver-isolation test with graphql subscribe() + pubsub.publish(...) to cover trigger / filter / error logic without a transport.
  7. Run under an extended testTimeout and --forceExit in CI so open sockets never hang the runner.

Authoring

Install

# WS transport
npm install --save-dev graphql-ws ws @types/ws

# SSE transport
npm install --save-dev graphql-sse

# Shared test utilities
npm install --save-dev graphql jest ts-jest

Server setup

Full server bootstraps for both transports (graphql-ws over ws, graphql-sse over Node http), including the port: 0 free-port pattern, are in references/transport-server-setup.md.

Basic subscription test (graphql-ws)

Per the-guild.dev/graphql/ws/get-started: both queries and subscriptions use client.iterate(), which returns an async iterator.

import { createClient } from 'graphql-ws';

describe('greetings subscription', () => {
  let wss: ReturnType<typeof startWsServer>;
  let client: ReturnType<typeof createClient>;

  beforeAll(() => {
    wss = startWsServer();
    const addr = wss.wss.address() as { port: number };
    client = createClient({ url: `ws://localhost:${addr.port}/graphql` });
  });

  afterAll(async () => {
    client.dispose();
    await wss.dispose();
    wss.wss.close();
  });

  it('streams three greetings then completes', async () => {
    const results: unknown[] = [];
    const sub = client.iterate({ query: 'subscription { greetings }' });

    for await (const event of sub) {
      results.push(event.data);
      if (results.length === 3) break; // break closes the stream
    }

    expect(results).toEqual([
      { greetings: 'Hi' },
      { greetings: 'Bonjour' },
      { greetings: 'Hola' },
    ]);
  });
});

Basic subscription test (graphql-sse)

Per the-guild.dev/graphql/sse/get-started:

import { createClient } from 'graphql-sse';

it('receives events over SSE', async () => {
  const { server, url } = startSseServer();
  const client = createClient({ url });

  const results: unknown[] = [];
  const sub = client.iterate({ query: 'subscription { greetings }' });

  for await (const event of sub) {
    results.push(event.data);
    if (results.length === 1) break;
  }

  expect(results[0]).toEqual({ greetings: 'Hi' });
  server.close();
});

Auth on connect

connectionParams (WS) and the authenticate callback (SSE) reject unauthenticated clients before any event is sent. Full server + test patterns for both transports, plus the async token-refresh factory, are in references/auth-on-connect.md.

Connection lifecycle and close codes

The graphql-ws protocol close-code table (4400 - 4429) and the ordered connecting -> connected -> closed lifecycle assertion are in references/lifecycle-and-close-codes.md.

Resolver, filter, and error tests

Resolver-isolation pubsub tests (via graphql subscribe()), event-filter predicate tests, and resolver-error propagation are in references/resolver-and-filter-tests.md.

Worked example

Scenario: a messageAdded(channel) subscription must stream only messages for the subscribed channel and must reject clients that connect without a token.

  1. Start the WS server (see the server-setup reference) with an onConnect that returns false on a missing token.
  2. Connect an authed client with connectionParams: { token: 'valid-token' } and open client.iterate({ query: 'subscription Messages($channel: String!) { messageAdded(channel: $channel) { text } } ', variables: { channel: 'team-a' } }).
  3. pubsub.publish a team-b message then a team-a message; await sub.next().
  4. Assert value.data?.messageAdded?.text === 'right' - the team-b event was filtered out, proving the predicate.
  5. Open a second client with connectionParams: {} and retryAttempts: 0; assert its closed event carries code 4403.

Result: one run proves both the resolver filter (only the matching channel emits) and the auth gate (tokenless connect is refused with 4403).

Running

npm test                         # jest / vitest pick up *.test.ts
npx jest subscriptions/ --verbose

Set a testTimeout for subscriptions - default 5 s is tight when the event loop needs to process WS frames:

// jest.config.ts
export default { testTimeout: 15_000 };

Parsing results

Both graphql-ws and graphql-sse client.iterate() yield objects shaped { data?: T; errors?: GraphQLError[] }. Check errors before asserting data:

const { value } = await sub.next();
expect(value.errors).toBeUndefined();
expect(value.data?.greetings).toBeDefined();

For the graphql package's subscribe() function, the iterator yields ExecutionResult objects. Multipart/incremental results are separate from standard subscription results.

CI integration

# .github/workflows/graphql-subscription-tests.yml
name: graphql-subscriptions
on: [pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npx jest subscriptions/ --forceExit --testTimeout=15000

--forceExit prevents Jest from hanging on open WS connections if a test fails before dispose() or close() is called.

Anti-patterns

Anti-patternWhy it failsFix
Hardcoded ws://localhost:4000Port conflicts in parallel CIport: 0, read back the OS-assigned port
No dispose() / close() in afterAllOpen WS/SSE connections prevent Jest from exitingAlways dispose client and close server
retryAttempts not set to 0 in rejection testsDefault retry masks the 4403 close eventSet retryAttempts: 0 for auth-rejection assertions
Asserting data without checking errorsMasked errors produce false positivesCheck errors === undefined before asserting data
Using executeOperation for subscription testsApollo in-process runner does not start a WS serverUse transport-layer client with createClient
Single combined test for subscribe + auth + filterFailures hard to diagnoseOne test per behavior
Testing only the transport, not the resolverResolver pubsub logic goes untestedCombine graphql.subscribe() unit tests with transport integration tests

Limitations

  • Resolver isolation tests require a testable pubsub instance. If the production pubsub is injected via context, pass a test double; if it is a module singleton, reset it in beforeEach.
  • WS close-code assertions are timing-sensitive. Use retryAttempts: 0 and wrap in a done callback (or Promise + event listener) rather than awaiting the iterator.
  • SSE in Node requires fetch or a polyfill. graphql-sse clients default to the global fetch; Node < 18 needs node-fetch or undici.
  • Does not cover HTTP-layer concerns. CORS, rate limiting, and response headers for the SSE endpoint need separate HTTP-layer tests (e.g., supertest).
  • Does not cover schema-contract drift. Pair with graphql-schema-regression (in the qa-contract-testing plugin) to catch subscription field renames between provider and consumer.

References

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

コメント

ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。

まだコメントはありません。最初のコメントを書いてみませんか。

送信するとすぐに公開されます。メールアドレスや電話番号などの個人情報は書かないでください。URL は 2 つまでです。

0 / 1000 文字

このフォームは reCAPTCHA で保護されており、Google のプライバシーポリシーと利用規約が適用されます。

同じリポジトリのスキル

Builds a CI gate that fails the build on **new** WCAG / a11y violations introduced by a PR while grandfathering pre-existing violations on a per-rule / per-page baseline. Aggregates verdicts from axe-core / pa11y / Lighthouse a11y / WAVE / IBM Equal Access scans. Use when a project has accumulated a11y debt and a strict "zero violations" gate would block every PR - the ratchet pattern lets the team ship while preventing regressions.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

Workflow skill that builds an A/B-test validity checklist from an experiment proposal, walking the canonical design-correctness gates - pre-registered OEC/power/guardrails, randomization unit + SRM check, assignment integrity, telemetry, peeking discipline, novelty/primacy, post-experiment SRM re-check - into a per-experiment checklist + sign-off form. Use when launching, auditing, or governing an experiment. For pitfall mechanics (guardrails, peeking) see experiment-results-interpreter's references; to read an already-valid result use experiment-results-interpreter; for per-SDK harness tests use experiment-sdk-testing - this gates DESIGN, not SDK code.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

The single install-flow skill: the reference contract for the PWA install flow (installability gate fields, `beforeinstallprompt` handshake, per-platform paths - Android WebAPK / iOS Share menu / Firefox no-op - and the `display-mode` post-install signal, in references/install-flow-reference.md) plus the build-an-X workflow that emits the Add-to-Home-Screen suite. Walks the four-stage timeline, emitting one test per gate cell per [web.dev/articles/install-criteria][install-criteria], the deferred-prompt → `prompt()` → `userChoice` chain per [web.dev/articles/customize-install][customize-install], the iOS Safari manual-metadata branch (`apple-touch-icon`) per [web.dev/learn/pwa/installation][learn-pwa], and the post-install `(display-mode: standalone)` MQ assertion. Output: a Playwright spec with per-stage cells plus a coverage matrix. Use when a PWA's manifest, icons, or install handler change, when install conversion drops at an unknown stage, or when triaging a flaky install assertion.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

Build-an-X workflow that uses an LLM to map existing tests to spec sections - given a spec doc + the test suite, the LLM identifies which tests cover which sections, surfaces uncovered sections (gap), and recommends specific tests to add. Output is a coverage matrix per spec ID. Scope is mapping tests that already exist and naming the gaps, not authoring tests for new acceptance criteria. Use when a spec doc and a test suite both exist but nobody can say which requirements are actually covered - before a release sign-off, an audit, or a decision about where to spend the next round of test effort.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

Generates tests from natural-language specs (acceptance criteria, user stories, requirements) using an LLM, with confidence scoring per test case (LLM self-assessment plus heuristics: assertion quality, naming, completeness), batching uncertain cases for human review, and integration with the team's existing test framework. Use when the user asks to generate unit tests from acceptance criteria, convert user stories to test cases, automate test creation from requirements, or augment a spec-driven test suite with AI-generated stubs that are then curated before merge.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

Configures Allure Report (test-runner adapter install, `allure-results` directory wiring, `categories.json` for failure classification, `history-trend.json` retention via the copy-history-between-runs pattern), runs the Allure CLI to convert `allure-results` to a static HTML site, and uploads the report as a CI artifact. Use when the team needs richer test reporting than JUnit XML - step-level attachments, per-test history, retry tracking, and severity / epic / feature labeling across framework-agnostic adapters (pytest, Jest, JUnit, TestNG, NUnit, Mocha). As a rich static HTML report generator, it is the open-source alternative to the sunset ExtentReports (JVM/.NET per-test HTML narrative); for hosted cross-run flakiness analytics rather than a static per-run report use currents-integration.

日本語の概要は準備中です。原文の説明を表示しています。

testland/qa132026年10月20日 更新

testland のスキルをすべて見る

このスキルの問題を報告する