GraphQL Server Testing with SuperTest
The `graphql-server-test` library provides utilities for performing constructive HTTP-level integration and end-to-end testing of GraphQL servers. Leveraging the popular `supertest` library, it enables developers to simulate actual HTTP requests against their GraphQL endpoint, ensuring that the entire server stack—including middleware, authentication, and database interactions—behaves as expected. Currently at version 2.12.6, this package is actively maintained and ships with TypeScript type definitions, facilitating type-safe test development. Its primary differentiator is its focus on black-box HTTP testing for GraphQL, making it suitable for any server implementation (e.g., Apollo Server, Express-GraphQL) by interacting with it as a standard HTTP service. This approach contrasts with unit testing individual resolvers, offering a more realistic assessment of the GraphQL API's functionality in production-like environments.
Common errors
-
TypeError: Schema must be an instance of GraphQLSchema.
cause The GraphQL server was initialized with an invalid schema object, or the schema was not correctly built before being passed to the server.fixEnsure your `typeDefs` and `resolvers` are correctly defined and processed by your GraphQL server library (e.g., `makeExecutableSchema` from `@graphql-tools/schema` or `new ApolloServer({ typeDefs, resolvers })`). Double-check imports for `GraphQLSchema`. -
Error: socket hang up (or similar network error like ECONNREFUSED)
cause The HTTP server was not running or properly listening on a port when `supertest` attempted to make a request, or the server instance was terminated prematurely.fixVerify that your server application is correctly started before tests run (`beforeAll`) and shut down afterward (`afterAll`). If using `supertest` with an Express app, ensure you pass the app instance directly to `request(app)` rather than a URL. -
HTTP Error: 500 Internal Server Error
cause A runtime error occurred within your GraphQL resolvers or server middleware that was not caught and formatted by the GraphQL server, resulting in a generic HTTP 500 status.fixInspect the server logs for the full stack trace of the internal server error. Add error handling and logging to your GraphQL resolvers and middleware to surface more specific error messages in the test response. -
GraphQL error: Cannot query field 'X' on type 'Y'.
cause The GraphQL query sent in the test is requesting a field that does not exist on the specified type in your GraphQL schema.fixCompare the problematic query in your test with your GraphQL schema definition. Correct the query to match the available fields and types in your schema, paying attention to casing and nesting.
Warnings
- breaking Major version updates (e.g., from v1 to v2) of `supertest` or underlying HTTP server frameworks (like Express or Apollo Server) can introduce breaking changes that might require updates to how the server application is configured or passed to `createTestClient`. Always review the changelogs of all dependent packages.
- gotcha Improper server lifecycle management in tests (e.g., not starting/stopping the HTTP server for each test suite or test file) can lead to 'address already in use' errors or resource leaks, especially with frameworks like Express or Apollo Server.
- gotcha GraphQL context in tests may not accurately reflect production if not explicitly set up. Authentication, authorization, and data source injection often depend on the context object, which needs to be properly mocked or constructed for tests.
- gotcha Asynchronous operations (queries, mutations) in GraphQL tests must be handled correctly with `async/await` to prevent flaky tests or incorrect assertions, as responses are Promises.
Install
-
npm install graphql-server-test -
yarn add graphql-server-test -
pnpm add graphql-server-test
Imports
- createTestClient
const createTestClient = require('graphql-server-test');import { createTestClient } from 'graphql-server-test'; - GraphQLTestClient
import { GraphQLTestClient } from 'graphql-server-test';import type { GraphQLTestClient } from 'graphql-server-test'; - gql
import { gql } from 'graphql-server-test';
Quickstart
import request from 'supertest';
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import express from 'express';
import http from 'http';
import { createTestClient, gql } from 'graphql-server-test';
interface MyContext { user?: { id: string, name: string }; }
const typeDefs = `#graphql
type User {
id: ID!
name: String!
}
type Query {
hello: String!
me: User
}
type Mutation {
createUser(name: String!): User!
}
`;
const users: { id: string, name: string }[] = [];
let nextId = 1;
const resolvers = {
Query: {
hello: () => 'world',
me: (_: any, __: any, context: MyContext) => context.user,
},
Mutation: {
createUser: (_: any, { name }: { name: string }) => {
const newUser = { id: String(nextId++), name };
users.push(newUser);
return newUser;
},
},
};
async function startApolloServer() {
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer<MyContext>({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
app.use('/graphql', express.json(), expressMiddleware(server, {
context: async ({ req }) => {
const token = req.headers.authorization || '';
// Mock user for testing purposes
if (token === 'Bearer testuser') {
return { user: { id: '123', name: 'Test User' } };
}
return {};
},
}));
return app;
}
describe('GraphQL Server Integration Tests', () => {
let app: express.Application;
let client: ReturnType<typeof createTestClient>;
beforeAll(async () => {
app = await startApolloServer();
client = createTestClient(request(app));
});
it('should return "world" for the hello query', async () => {
const response = await client.query(gql`query { hello }`);
expect(response.status).toBe(200);
expect(response.body.data.hello).toBe('world');
});
it('should return the authenticated user', async () => {
const response = await client.query(
gql`query { me { id name } }`,
{ headers: { Authorization: 'Bearer testuser' } }
);
expect(response.status).toBe(200);
expect(response.body.data.me).toEqual({ id: '123', name: 'Test User' });
});
it('should create a new user via mutation', async () => {
const response = await client.mutate(
gql`mutation CreateUser($name: String!) { createUser(name: $name) { id name } }`,
{ variables: { name: 'New User' } }
);
expect(response.status).toBe(200);
expect(response.body.data.createUser).toHaveProperty('id');
expect(response.body.data.createUser.name).toBe('New User');
});
});