Skip to content

Latest commit

 

History

History

README.md

graphile-test

Image

Image Image Image

graphile-test builds on top of pgsql-test to provide robust GraphQL testing utilities for PostGraphile-based projects.

It provides a seamless setup for isolated, seeded, role-aware Postgres databases and injects GraphQL helpers for snapshot testing, role context, and mutation/query assertions.

Note: This is a bare-bones package with no defaults or settings applied. For a batteries-included version with all Constructive plugins pre-configured, use @constructive-io/graphql-test instead.

🚀 Features

  • 🔁 Per-test rollback via savepoints for isolation
  • 🔐 RLS-aware context injection (setContext)
  • 🧪 GraphQL integration testing with query() and snapshot support
  • 📦 Seed support for .sql, JSON, CSV, Constructive, or Sqitch
  • 📊 Introspection query snapshotting
  • 🔧 Raw SQL fallback via pg.client.query

📦 Install

npm install graphile-test

✨ Quick Start

import { getConnections, seed } from 'graphile-test';

let db, query, teardown;

beforeAll(async () => {
  ({ db, query, teardown } = await getConnections({
    schemas: ['app_public'],
    authRole: 'authenticated'
  }, [
    seed.sqlfile(['../sql/test.sql', '../sql/grants.sql'])
  ]));
});

beforeEach(() => db.beforeEach());
afterEach(() => db.afterEach());
afterAll(() => teardown());

it('runs a GraphQL mutation', async () => {
  const res = await query(`mutation { ... }`, { input: { ... } });
  expect(res.data.createUser.username).toBe('alice');
});

📘 API

getConnections(options, seeders)

Returns an object with:

  • query(gqlString, variables?) – A GraphQL executor function with positional arguments
  • db, pgPgTestClient instances
  • teardown() – Clean up temp DBs

Basic Usage:

const result = await query(`mutation { ... }`, { input: { ... } });
expect(result.data.createUser.username).toBe('alice');

PgTestClient

Supports:

  • query, any, one, etc. (via pg-promise-like helpers)
  • beforeEach() / afterEach() – for savepoint transaction handling
  • setContext({...}) – sets Postgres config (e.g., role, myapp.user_id)

See full PgTestClient API docs: pgsql-test → PgTestClient API Overview

🧪 Example Tests

GraphQL mutation + snapshot

const res = await query(`mutation { ... }`, { input: { ... } });
expect(snapshot(res.data)).toMatchSnapshot();

RLS testing with role switch

db.setContext({ role: 'anonymous' });
const res = await query(`query { ... }`);
expect(res.errors[0].message).toMatch(/permission denied/);

Typed queries for better safety

interface CreateUserVariables {
  input: {
    user: {
      username: string;
    };
  };
}

interface CreateUserResult {
  createUser: {
    user: {
      id: number;
      username: string;
    };
  };
}

const res = await query<CreateUserResult, CreateUserVariables>(`
    mutation CreateUser($input: CreateUserInput!) {
      createUser(input: $input) {
        user {
          id
          username
        }
      }
    }
  `,
  { input: { user: { username: 'alice' } } }
);

expect(res.data?.createUser.user.username).toBe('alice');

🔧 Advanced Connection Options

For specific testing needs, additional connection functions are available:

Error Handling Variants

  • getConnectionsUnwrapped() – Automatically throws on GraphQL errors, returns data directly

Debugging Variants

  • getConnectionsWithLogging() – Logs all queries and responses
  • getConnectionsWithTiming() – Times query execution

Object-Based API

  • getConnectionsObject() – Uses query({ query: "...", variables: {} }) syntax
  • getConnectionsObjectUnwrapped() – Object-based with automatic error throwing

Unwrapped Example (cleaner assertions):

import { getConnectionsUnwrapped } from 'graphile-test';

const { query } = await getConnectionsUnwrapped(config);

// Throws automatically on GraphQL errors, returns data directly
const result = await query(`mutation { ... }`, { input: { ... } });
expect(result.createUser.username).toBe('alice'); // No .data needed!

Object-Based Example:

import { getConnectionsObject } from 'graphile-test';

const { query } = await getConnectionsObject(config);

const result = await query({ 
  query: `mutation { ... }`, 
  variables: { input: { ... } } 
});
expect(result.data.createUser.username).toBe('alice');

🧱 Under the Hood

graphile-test wraps and extends pgsql-test with GraphQL helpers like query() and introspection snapshot tools. You can drop into raw SQL testing anytime via pg.client.query() (superuser) or db.client.query() (RLS user).

✅ Best Practices

  • Use db.setContext({ role, user_id }) to simulate authentication.
  • Always wrap tests with beforeEach / afterEach.
  • Use snapshot() to track GraphQL result changes.
  • Use useRoot: true to test schema visibility without RLS.
  • Start with getConnections() for most use cases.
  • Consider getConnectionsUnwrapped() for cleaner test assertions.

Snapshot Utilities

The graphile-test/utils module provides utilities for sanitizing query results for snapshot testing. These helpers replace dynamic values (IDs, UUIDs, dates, hashes) with stable placeholders, making snapshots deterministic.

import { snapshot } from 'graphile-test/utils';

const res = await query(`query { allUsers { nodes { id name createdAt } } }`);
expect(snapshot(res.data)).toMatchSnapshot();

See pgsql-test Snapshot Utilities for the full API reference.

🧪 Testing

# requires a local Postgres available (defaults to postgres/password@localhost:5432)
pnpm --filter graphile-test test