AlgoInsight

Q&A

How to test your Apollo Links

By AlgoInsight Team2026-06-18testing

Overview

An Apollo Link is a function that takes a GraphQL operation and a "forward" function (the next link in the chain) and returns an Observable. Testing a link means verifying that it correctly transforms the operation, the context, or the response - without needing a real server. The key is constructing a minimal, deterministic chain where you control the "end" of the pipe.

graph LR
    A[Your Link under test] --> B[Mock Terminating Link]
    C["execute(chain, operation)"] --> A
    B --> D["Observable.of(mockResponse)"]
    D --> E[Subscribe and assert]
    style A fill:#1e1b4b,color:#fff

Setting Up: Minimal Test Chain

A link chain needs a terminating link - one that doesn't call forward() but instead returns data. ApolloLink.from assembles the chain:

import { ApolloLink, execute, Observable } from "@apollo/client";
import { gql } from "@apollo/client/core";

// Create a mock terminating link that returns a fixed response:
const mockTerminator = new ApolloLink(() =>
  Observable.of({ data: { user: { id: "1", name: "Ada" } } })
);

// Your link under test (e.g. an auth header link):
const authLink = new ApolloLink((operation, forward) => {
  operation.setContext(({ headers = {} }) => ({
    headers: { ...headers, authorization: "Bearer test-token" },
  }));
  return forward(operation);
});

// Chain: authLink → mockTerminator
const chain = ApolloLink.from([authLink, mockTerminator]);

Running the Chain and Asserting

Use execute() to run an operation through the chain and subscribe to the Observable result:

const op = { query: gql`{ user { id name } }` };

const result = await new Promise((resolve, reject) => {
  execute(chain, op).subscribe({
    next: resolve,
    error: reject,
  });
});

expect(result).toEqual({ data: { user: { id: "1", name: "Ada" } } });

Testing What Your Link Does to the Request

Capture what the link passed to forward():

let capturedOperation: any;

const capturingTerminator = new ApolloLink((op) => {
  capturedOperation = op;
  return Observable.of({ data: {} });
});

const chain = ApolloLink.from([authLink, capturingTerminator]);

execute(chain, { query: gql`{ me { id } }` }).subscribe(() => {
  const headers = capturedOperation.getContext().headers;
  expect(headers.authorization).toBe("Bearer test-token");
});

Testing an Error Link

const errorLink = onError(({ graphQLErrors, networkError }) => {
  // ...error handling
});

// Terminating link that returns an error:
const errorTerminator = new ApolloLink(() =>
  Observable.of({ errors: [{ message: "Not authorized" }] })
);

const chain = ApolloLink.from([errorLink, errorTerminator]);
// Assert error handler was called, or that the operation was retried

Using MockLink for End-to-End Tests

@apollo/client/testing provides MockLink for component/integration tests:

import { MockedProvider, MockLink } from "@apollo/client/testing";
import { GET_USER } from "./queries";

const mocks = [{
  request: { query: GET_USER, variables: { id: "1" } },
  result: { data: { user: { id: "1", name: "Ada" } } },
}];

// In React component testing:
render(
  <MockedProvider mocks={mocks} addTypename={false}>
    <UserProfile userId="1" />
  </MockedProvider>
);

await waitFor(() => {
  expect(screen.getByText("Ada")).toBeInTheDocument();
});

MockedProvider replaces the real Apollo Client with one that matches operations to canned responses - no network calls.

Jest Setup Tips

// Helper to await a link chain result:
function executeLink(link: ApolloLink, op: any): Promise<any> {
  return new Promise((resolve, reject) =>
    execute(link, op).subscribe({ next: resolve, error: reject })
  );
}

Why This Matters in Interviews

Apollo/GraphQL interviews use this to check that you understand the link middleware model. Key points: links are composable; test in isolation by creating a mock terminating link; use execute() + subscribe to trigger the chain; capture the operation passed to forward() to assert on header/context transforms; and use MockedProvider for component-level integration tests. Understanding the Observable-based API is the underlying concept.

Common Pitfalls

References

More answers