Testland
Browse all skills & agents

pothos-builder-tests

Wraps Pothos GraphQL schema-builder testing patterns: testing the SchemaBuilder output (lexicographicSortSchema + printSchema for snapshot tests), testing resolvers via the standard `graphql()` function from graphql-js (no server needed), integration with Apollo Server / GraphQL Yoga (Pothos emits standard graphql-js schemas), and code-first builder unit tests. Covers the SchemaBuilder API surface (queryType, mutationType, objectType, t.field, t.arg). Use when testing a Pothos-built schema before or alongside the server-runtime tests.

Install with skills.sh (any agent)

npx skills add testland/qa --skill pothos-builder-tests
View source

pothos-builder-tests

Overview

Per pothos-graphql.dev (opens in new window), "the schema generated by Pothos is a standard graphql.js schema" - so any GraphQL test pattern works against it. The testing opportunity unique to Pothos is testing the builder output shape (which types, which fields, which deprecations) and the schema-stability over refactors.

When to use

  • Unit tests for resolvers built with Pothos.
  • Snapshot tests for the printed schema (catch unintended schema changes).
  • Combining a Pothos schema with Apollo Server / Yoga in integration tests.
  • PR review of changes to Pothos type definitions.

Authoring

Install

npm install --save-dev @pothos/core graphql

Build a test schema

Per pothos-graphql.dev/docs/guide (opens in new window):

import SchemaBuilder from '@pothos/core';

const builder = new SchemaBuilder({});

builder.queryType({
  fields: (t) => ({
    hello: t.string({
      args: { name: t.arg.string() },
      resolve: (_parent, { name }) => `hello, ${name || 'World'}`,
    }),
  }),
});

export const schema = builder.toSchema();

The schema is a plain graphql.GraphQLSchema instance.

Unit-test a resolver via graphql()

import { graphql } from 'graphql';
import { schema } from './schema';

test('hello resolver', async () => {
  const result = await graphql({
    schema,
    source: `{ hello(name: "alice") }`,
  });
  expect(result.errors).toBeUndefined();
  expect(result.data?.hello).toBe('hello, alice');
});

graphql() from graphql-js executes against the schema directly. No server. No HTTP. No middleware. Fast unit-test path.

Schema snapshot test

Catch any change to the public schema:

import { lexicographicSortSchema, printSchema } from 'graphql';
import { schema } from './schema';

test('schema snapshot', () => {
  // lexicographicSortSchema produces deterministic ordering
  const printed = printSchema(lexicographicSortSchema(schema));
  expect(printed).toMatchSnapshot();
});

When the schema changes, the snapshot fails and reviewers see the diff. Refactor-safe: a resolver rename that doesn't touch the schema doesn't trigger the snapshot.

Integration with Apollo Server

import { ApolloServer } from '@apollo/server';
import { schema } from './schema';

const server = new ApolloServer({ schema });
// ... use apollo-server-tests patterns from here

Integration with GraphQL Yoga

import { createYoga } from 'graphql-yoga';
import { schema } from './schema';

const yoga = createYoga({ schema });
// ... use graphql-yoga-tests patterns from here

Running

npm test
npm test -- --updateSnapshot       # accept schema changes

Testing the SchemaBuilder's plugin contract

Pothos has plugins for relay, prisma, errors, etc. Test each plugin's output:

import RelayPlugin from '@pothos/plugin-relay';

const builder = new SchemaBuilder<{ Context: AuthContext }>({
  plugins: [RelayPlugin],
  relay: { clientMutationId: 'optional' },
});

builder.queryType({ /* ... */ });
const schema = builder.toSchema();

test('relay plugin adds Node interface', () => {
  const printed = printSchema(lexicographicSortSchema(schema));
  expect(printed).toContain('interface Node');
});

Testing context-required resolvers

test('me resolver returns current user', async () => {
  const result = await graphql({
    schema,
    source: `{ me { id name } }`,
    contextValue: { user: { id: 'u1', name: 'alice' } },
  });
  expect(result.data?.me).toEqual({ id: 'u1', name: 'alice' });
});

test('me resolver errors without auth', async () => {
  const result = await graphql({
    schema,
    source: `{ me { id name } }`,
    contextValue: { user: null },
  });
  expect(result.errors?.[0].message).toMatch(/authenticate/i);
});

Parsing results

graphql() returns an ExecutionResult:

{
  data?: { ... },         // null on error if root resolver failed
  errors?: GraphQLError[],
  extensions?: { ... }
}

Snapshot tests use expect(printed).toMatchSnapshot(). The diff on failure shows exactly which types/fields changed.

CI integration

jobs:
  pothos:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - run: npm ci
      - run: npm test
      - name: Schema snapshot must match
        run: |
          # Fails the build if snapshot would change
          npx jest --ci tests/schema-snapshot.test.ts

The --ci flag prevents --updateSnapshot and fails on mismatch.

Anti-patterns

Anti-patternWhy it failsFix
Skipping lexicographicSortSchema for snapshotsNon-deterministic order → snapshot flakesAlways sort
Using --updateSnapshot in CIHides real schema changes--ci flag only
Testing the builder API instead of the outputPothos plugins drift; tests pass against deprecated builderTest the printed schema
Single mega-snapshot for the whole schemaOne field change → unrelated review painPer-domain snapshots (Query, Mutation, types)
No contextValue in resolver testsTests bypass auth; passes for unauthenticated pathsAlways pass test context
Server-runtime tests without Pothos-builder testsSchema regressions slip through unit testsBoth layers needed
Testing resolvers in isolation onlyMisses how plugins (relay, errors) reshape the responseBoth unit + integration
Pothos schema diverges between dev and prodDifferent plugins / configsBuild prod schema in test setup

Limitations

  • No HTTP behaviour. graphql() skips transport; for header / middleware testing use apollo-server-tests or graphql-yoga-tests.
  • Plugin combination explodes. Each Pothos plugin changes the output; test the actual combination used in prod.
  • TypeScript strict mode required. Per Pothos docs: "strict mode is essential." Tests must compile under strict.
  • Snapshot file proliferation. Multiple snapshot files for multiple build configurations can drift; manage carefully.
  • Doesn't replace contract testing. Internal schema stability isn't external compatibility - pair with graphql-schema-regression (in the qa-contract-testing plugin).

References

Related skills

apollo-server-tests

Wraps Apollo Server testing patterns: `server.executeOperation()` (in-process, no HTTP), `supertest` against an ephemeral-port HTTP server (port 0), context injection via the `contextValue` second-argument, and assertion patterns for response shape + errors. Includes the production-config gates testable through this skill - introspection-disabled, persisted-query mode, hideSchemaDetailsFromClientErrors. Use when writing tests for an Apollo Server v4+ GraphQL service.

graphql-complexity-limit-tester

Crafts over-limit depth and complexity queries then asserts rejection before execution, verifying that graphql-depth-limit, graphql-cost-analysis, and graphql-armor (max-depth / cost-limit / max-tokens plugins) are actually enforced and not just configured. Use when auditing a GraphQL service for DoS exposure after depth or cost limits have been added as mitigations, or when adding tests that prove the limits in CI before a production deployment.

graphql-n-plus-one-remediation

Traces a GraphQL resolver tree to locate the N+1 pattern (one parent query returns N rows, then a child field resolver fires once per row), classifies every child field resolver as safe or N+1 risk, and applies one of three fixes: per-request DataLoader batching, eager projection in the parent resolver, or selection-set-aware prefetch. Use when a list-returning resolver is added or changed in review, when a connection-pool exhaustion or slow-query alert traces back to GraphQL traffic, or when a resolver trace shows a child field resolved once per parent row.

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, graphql-yoga-tests, or mercurius-tests.

graphql-yoga-tests

Tests a GraphQL Yoga server (the-guild.dev runtime) with `yoga.fetch()` for in-process, no-network request simulation of queries and mutations, `@graphql-tools/executor-http` for subscription and incremental-delivery (streaming) tests, auth-header pass-through, and production-config gates for disabled introspection and persisted operations. Use to test a GraphQL Yoga server, write Yoga query, mutation, or subscription tests, or check its production plugin config; for a different runtime harness use apollo-server-tests, mercurius-tests, or hasura-tests instead, not this skill.

hasura-tests

Wraps Hasura GraphQL Engine testing patterns: docker-compose test instance, the metadata API for declarative schema/permission setup, x-hasura-role and x-hasura-user-id session headers for role-based permission tests, the v1/graphql endpoint via curl/HTTPie/native HTTP clients, and the role-by-table-by-operation permission-matrix pattern. Use for a metadata-driven Hasura engine where row-level permissions dominate; for a code-first server runtime harness use graphql-yoga-tests, apollo-server-tests, or mercurius-tests instead, not this skill.

introspection-attack-surface-reference

Pure-reference catalog of GraphQL introspection as an attack surface and the production-deployment controls for it. Covers what introspection exposes (every type, field, directive, deprecation, description via __schema / __type), Apollo Server's default behaviour (introspection: false when NODE_ENV=production), the `hideSchemaDetailsFromClientErrors: true` companion setting (strips 'did you mean' suggestions), Yoga / Mercurius / Hasura equivalents, query-depth + query-cost limits, persisted-query allowlisting as the strongest mitigation, and the testable behaviours each control creates. Use when designing the production-safety posture of a GraphQL server or auditing an existing deployment.

mercurius-tests

Wraps Mercurius (Fastify GraphQL plugin) testing patterns: `app.inject()` for HTTP-layer simulation without spinning up a network listener, plugin-registration setup (await app.register(mercurius, { schema, resolvers, graphiql: false })), production-config gates (graphiql: false; jit threshold; query depth limits via fastify-rate-limit + complexity), and the per-test app lifecycle (app.close() in afterEach). Use when writing tests for a Fastify + Mercurius GraphQL server.

persisted-query-strategy-reference

Pure-reference catalog of GraphQL Persisted Query strategies: the Apollo APQ SHA-256 hash protocol, the PersistedQueryNotFoundError retry flow, the `extensions.persistedQuery` payload, GET-vs-POST and CDN-cache implications, and the three operation modes (auto-register, strict allowlist, hybrid). Use to design or audit a server's persisted-query request layer. This strategy reference emits no tests; to author the runtime tests use apollo-server-tests, graphql-yoga-tests, or mercurius-tests, and for introspection lockdown see introspection-attack-surface-reference.