How to Test Apollo Client in React

Testing React components that integrate with Apollo Client is essential for ensuring your UI correctly handles GraphQL queries, mutations, loading states, and error conditions. This article provides a direct, step-by-step guide on how to use MockedProvider from @apollo/client/testing alongside React Testing Library to simulate GraphQL network traffic and verify your component behavior without a live backend.

Setting Up the MockedProvider

The @apollo/client/testing package provides a MockedProvider component. This utility acts as a replacement for the standard ApolloProvider in your test files. Instead of sending actual network requests to a GraphQL endpoint, MockedProvider intercepts queries and mutations and returns pre-configured mock data.

To test a component, wrap it inside MockedProvider and pass an array of mock objects to its mocks prop.

Defining Mock Data

Each mock object in your mocks array must precisely match the GraphQL request your component makes. A mock contains two main properties: * request: Specifies the exact query or mutation and its associated variables. * result: Specifies the mock data or error payload that should be returned.

Here is an example of a mock configuration:

import { GET_USER } from './UserComponent';

const mocks = [
  {
    request: {
      query: GET_USER,
      variables: { id: '1' },
    },
    result: {
      data: {
        user: { id: '1', name: 'John Doe', email: 'john@example.com' },
      },
    },
  },
];

Writing the Test Case

When writing your tests with React Testing Library, you should test the different states of your component: loading, success, and error. Because GraphQL requests are asynchronous, you must use asynchronous utilities like findByText or waitFor to assert the final rendered state.

import React from 'react';
import { render, screen } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import { UserComponent, GET_USER } from './UserComponent';

const mocks = [
  {
    request: {
      query: GET_USER,
      variables: { id: '1' },
    },
    result: {
      data: {
        user: { id: '1', name: 'John Doe', email: 'john@example.com' },
      },
    },
  },
];

test('renders loading and then success state', async () => {
  render(
    <MockedProvider mocks={mocks} addTypename={false}>
      <UserComponent id="1" />
    </MockedProvider>
  );

  // 1. Verify loading state is displayed initially
  expect(screen.getByText(/loading/i)).toBeInTheDocument();

  // 2. Wait for the mock response to resolve and verify UI changes
  const userName = await screen.findByText('John Doe');
  expect(userName).toBeInTheDocument();
  expect(screen.queryByText(/loading/i)).not.toBeInTheDocument();
});

Note: Passing addTypename={false} to the MockedProvider prevents Apollo Client from automatically adding __typename fields to your queries, which makes writing mock objects simpler.

Testing Error States

To test how your component behaves when a GraphQL operation fails, you can simulate either a network error or a GraphQL execution error inside your mock definition.

Simulating a GraphQL Error

You can return an errors array in the mock result object to simulate backend validation or execution failures.

const errorMock = [
  {
    request: {
      query: GET_USER,
      variables: { id: '1' },
    },
    result: {
      errors: [new Error('GraphQL Error Occurred')],
    },
  },
];

Simulating a Network Error

To simulate a complete network or connection failure, provide an error property directly on the mock object instead of a result property.

const networkErrorMock = [
  {
    request: {
      query: GET_USER,
      variables: { id: '1' },
    },
    error: new Error('A network error occurred'),
  },
];

By verifying these various states with MockedProvider, you ensure your React application remains resilient and handles data fetching predictably across all scenarios.