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 / alongside the server-runtime tests (apollo-server-test / graphql-yoga-test). Pairs with introspection-attack-surface-reference for production-config assertions via the underlying server.

pothos-builder-tests

Overview

Per pothos-graphql.dev, "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:

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-test patterns from here

Integration with GraphQL Yoga

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

const yoga = createYoga({ schema });
// ... use graphql-yoga-test 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-test or graphql-yoga-test.
  • 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 qa-contract-testing/graphql-schema-regression.

References