Apollo Server Integration Testing Utilities

raw JSON →
3.0.0 verified Sun Apr 19 auth: no javascript

This package provides essential utilities for conducting true integration tests on Apollo Server instances, especially when the server's `context` option depends on mocked HTTP `Request` or `Response` objects. Unlike the now-deprecated `apollo-server-testing` package, which often resulted in `req` being undefined in the context function, `apollo-server-integration-testing` automatically provides robust mock objects, enabling comprehensive testing of server logic, including middleware and resolvers that access `req` or `res`. The package's current stable version is 3.0.0, and while it doesn't have a fixed release cadence, it generally aims to support relevant versions of `graphql` (from 0.12.0 up to 15.0.0 as per its peer dependencies) to remain compatible with various Apollo Server setups. Its key differentiator is its ability to simulate a full HTTP request/response cycle, making it suitable for integration and end-to-end testing where `ApolloServer.executeOperation` (recommended for simpler unit tests) is insufficient.

error Cannot read properties of undefined (reading 'req') / TypeError: Cannot read property 'req' of undefined
cause Attempting to access `req` (or `res`) within the `ApolloServer`'s `context` function when using `apollo-server-testing` or `ApolloServer.executeOperation` for a test where `req` is not explicitly mocked.
fix
Switch to apollo-server-integration-testing for tests that require req or res objects in the context, or manually mock the context argument when using ApolloServer.executeOperation if a full HTTP mock isn't needed. Alternatively, if using apollo-server-integration-testing, ensure extendMockRequest or setOptions provide the necessary req properties.
error ApolloServer must be started before it can be used. Call `await server.start()` before `applyMiddleware()` or `listen()`.
cause When using Apollo Server v3 or later, the `server.start()` method must be called to initialize the server before any operations or integrations can use it. This error indicates it was omitted before `createTestClient` was invoked.
fix
Add await apolloServer.start(); after creating your new ApolloServer(...) instance and before passing apolloServer to createTestClient.
gotcha When testing Apollo Server `context` functions that rely on `req` or `res` objects, using the official `apollo-server-testing` package (now deprecated in favor of `server.executeOperation`) will result in `req` being `undefined`. This package (`apollo-server-integration-testing`) is specifically designed to address this limitation by providing robust mock HTTP request/response objects.
fix Use `apollo-server-integration-testing` for integration tests where your `ApolloServer` instance's `context` option is a function that processes the incoming `req` or `res` objects. For simpler, isolated GraphQL operation tests, consider `ApolloServer.executeOperation` directly.
breaking Apollo Server v3 and later versions require calling `await server.start()` before the server can be used. Failing to do so will result in runtime errors. This applies when you are passing an `ApolloServer` instance to `createTestClient`.
fix Ensure you call `await apolloServer.start()` after creating your `ApolloServer` instance and before passing it to `createTestClient`.
gotcha The `graphql` peer dependency for `apollo-server-integration-testing` has a wide range (`^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0`). However, the specific version of `graphql` required by your `Apollo Server` package (e.g., Apollo Server 3 requires `graphql` v15.3.0+, Apollo Server 4 requires `graphql` v16+) might be more restrictive. A mismatch can lead to unexpected behavior or conflicts.
fix Always install a `graphql` version that satisfies both `apollo-server-integration-testing`'s peer dependency and, more importantly, the explicit requirements of your specific `Apollo Server` package.
npm install apollo-server-integration-testing
yarn add apollo-server-integration-testing
pnpm add apollo-server-integration-testing

This quickstart demonstrates how to create an Apollo Server, use `createTestClient` to run queries and mutations against it, mock and extend the `Request` object for context testing, and dynamically update mock options using `setOptions`. It includes mandatory `server.start()` and `server.stop()` calls for Apollo Server v3+.

import { createTestClient } from 'apollo-server-integration-testing';
import { ApolloServer } from '@apollo/server';
import { buildSubgraphSchema } from '@apollo/subgraph';
import gql from 'graphql-tag';

const typeDefs = gql`
  type User {
    id: ID!
    email: String
  }

  type Query {
    currentUser: User
  }

  type Mutation {
    updateUser(id: ID!, email: String!): User
  }
`;

const resolvers = {
  Query: {
    currentUser: (_, __, { req }) => {
      // Simulate context logic that depends on req
      if (req?.headers?.authorization) {
        return { id: '1', email: 'test@example.com' };
      }
      return null;
    },
  },
  Mutation: {
    updateUser: (_, { id, email }) => ({ id, email }),
  },
};

async function createApolloServer() {
  const server = new ApolloServer({
    schema: buildSubgraphSchema({ typeDefs, resolvers }),
  });
  await server.start(); // Mandatory for Apollo Server v3+
  return server;
}

describe('Apollo Server Integration Tests', () => {
  let apolloServer;
  let query;
  let mutate;
  let setOptions;

  beforeAll(async () => {
    apolloServer = await createApolloServer();
    ({ query, mutate, setOptions } = createTestClient({
      apolloServer,
      extendMockRequest: {
        headers: { authorization: 'Bearer token' }
      }
    }));
  });

  afterAll(async () => {
    await apolloServer.stop();
  });

  test('should fetch current user with mocked request headers', async () => {
    const result = await query(`{ currentUser { id email } }`);
    expect(result).toEqual({
      data: {
        currentUser: {
          id: '1',
          email: 'test@example.com'
        }
      }
    });
  });

  test('should update user and allow subsequent request modification', async () => {
    setOptions({
      request: { headers: { authorization: 'Bearer another-token' } }
    });

    const UPDATE_USER_MUTATION = `
      mutation UpdateUser($id: ID!, $email: String!) {
        updateUser(id: $id, email: $email) {
          id
          email
        }
      }
    `;
    const mutationResult = await mutate(UPDATE_USER_MUTATION, {
      variables: { id: '1', email: 'jane.doe@example.com' }
    });

    expect(mutationResult).toEqual({
      data: {
        updateUser: {
          id: '1',
          email: 'jane.doe@example.com'
        }
      }
    });
  });
});