How to Test GraphQL in React

Testing GraphQL in React is essential for ensuring your application fetches, mutates, and displays data correctly. This article provides a straightforward guide on how to test GraphQL queries and mutations in React components using Jest, React Testing Library, and Apollo Client’s testing utilities. You will learn how to mock GraphQL network requests, verify loading and error states, and assert that the correct data renders on the screen.

The Testing Toolset

To test GraphQL in a React application, you need three primary tools: 1. Jest: The test runner and assertion library. 2. React Testing Library (RTL): For rendering components and interacting with the DOM. 3. MockedProvider: A utility provided by @apollo/client/testing that mocks GraphQL network traffic so you do not have to make real API calls during tests.


Step 1: Create the Component to Test

Consider a simple component named UserCharacter that fetches a character’s name from a GraphQL API using the useQuery hook.

import React from 'react';
import { useQuery, gql } from '@apollo/client';

export const GET_CHARACTER = gql`
  query GetCharacter($id: ID!) {
    character(id: $id) {
      id
      name
    }
  }
`;

export function UserCharacter({ id }) {
  const { loading, error, data } = useQuery(GET_CHARACTER, {
    variables: { id },
  });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error loading character.</p>;

  return <h1>{data.character.name}</h1>;
}

Step 2: Define the GraphQL Mocks

To test this component, you must mock the network response. A mock in Apollo Client is an object containing a request (the query and variables) and a result (the simulated data returned by the API).

import { GET_CHARACTER } from './UserCharacter';

const mocks = [
  {
    request: {
      query: GET_CHARACTER,
      variables: { id: '1' },
    },
    result: {
      data: {
        character: { id: '1', name: 'Luke Skywalker' },
      },
    },
  },
];

Step 3: Write the Tests

When testing components with MockedProvider, you must wrap your component with it and pass the mocks array as a prop. Because GraphQL queries are asynchronous, you will need to use asynchronous utilities from React Testing Library to wait for the data to render.

Here is the complete test file:

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

const mocks = [
  {
    request: {
      query: GET_CHARACTER,
      variables: { id: '1' },
    },
    result: {
      data: {
        character: { id: '1', name: 'Luke Skywalker' },
      },
    },
  },
];

describe('UserCharacter Component', () => {
  it('should render loading state initially', () => {
    render(
      <MockedProvider mocks={mocks} addTypename={false}>
        <UserCharacter id="1" />
      </MockedProvider>
    );

    expect(screen.getByText('Loading...')).toBeInTheDocument();
  });

  it('should render the character name after loading', async () => {
    render(
      <MockedProvider mocks={mocks} addTypename={false}>
        <UserCharacter id="1" />
      </MockedProvider>
    );

    // Use findByText to wait for the asynchronous update
    const characterName = await screen.findByText('Luke Skywalker');
    expect(characterName).toBeInTheDocument();
  });

  it('should render an error state when the query fails', async () => {
    const errorMock = [
      {
        request: {
          query: GET_CHARACTER,
          variables: { id: '1' },
        },
        error: new Error('An error occurred'),
      },
    ];

    render(
      <MockedProvider mocks={errorMock} addTypename={false}>
        <UserCharacter id="1" />
      </MockedProvider>
    );

    const errorElement = await screen.findByText('Error loading character.');
    expect(errorElement).toBeInTheDocument();
  });
});

Key Best Practices