Testing React components

Using MockedProvider and associated APIs


This article describes best practices for testing React components that use Apollo Client.

The examples below use Jest and React Testing Library, but the concepts apply to any testing framework.

The MockedProvider component

Every test for a React component that uses Apollo Client must make Apollo Client available on React's context. In application code, you achieve this by wrapping your component tree with the ApolloProvider component. In your tests, you use the MockedProvider component instead.

The MockedProvider component enables you to define mock responses for individual queries that are executed in your test. This means your test doesn't need to communicate with a GraphQL server, which removes an external dependency and therefore improves the test's reliability.

Example

Let's say we want to test the following Dog component, which executes a basic query and displays its result:

Click to expand 🐶
JavaScript
dog.jsx
1import React from "react";
2import { gql, useQuery } from "@apollo/client";
3
4// Make sure that both the query and the component are exported
5export const GET_DOG_QUERY = gql`
6  query GetDog($name: String) {
7    dog(name: $name) {
8      id
9      name
10      breed
11    }
12  }
13`;
14
15export function Dog({ name }) {
16  const { loading, error, data } = useQuery(GET_DOG_QUERY, {
17    variables: { name }
18  });
19  if (loading) return <p>Loading...</p>;
20  if (error) return <p>{error.message}</p>;
21  return (
22    <p>
23      {data.dog.name} is a {data.dog.breed}
24    </p>
25  );
26}

A basic rendering test for the component looks like this (minus mocked responses):

JavaScript
dog.test.js
1import "@testing-library/jest-dom";
2import { render, screen } from "@testing-library/react";
3import { MockedProvider } from "@apollo/client/testing";
4import { GET_DOG_QUERY, Dog } from "./dog";
5
6const mocks = []; // We'll fill this in next
7
8it("renders without error", async () => {
9  render(
10    <MockedProvider mocks={mocks} addTypename={false}>
11      <Dog name="Buck" />
12    </MockedProvider>
13  );
14  expect(await screen.findByText("Loading...")).toBeInTheDocument();
15});

Note: Usually, you import @testing-library/jest-dom in your test setup file, which provides certain custom jest matchers (such as toBeInTheDocument). The import is included in these examples for completeness.

Defining mocked responses

The mocks prop of MockedProvider is an array of objects, each of which defines the mock response for a single operation. Let's define a mocked response for GET_DOG_QUERY when it's passed the name Buck:

JavaScript
dog.test.js
1const mocks = [
2  {
3    request: {
4      query: GET_DOG_QUERY,
5      variables: {
6        name: "Buck"
7      }
8    },
9    result: {
10      data: {
11        dog: { id: "1", name: "Buck", breed: "bulldog" }
12      }
13    }
14  }
15];

Each mock object defines a request field (indicating the shape and variables of the operation to match against) and a result field (indicating the shape of the response to return for that operation).

Your test must execute an operation that exactly matches a mock's shape and variables to receive the associated mocked response.

Alternatively, the result field can be a function that returns a mocked response after performing arbitrary logic:

JavaScript
1result: (variables) => { // `variables` is optional
2  // ...arbitrary logic...
3
4  return {
5    data: {
6      dog: { id: '1', name: 'Buck', breed: 'bulldog' },
7    },
8  }
9},

Combining our code above, we get the following complete test:

Click to expand 🐶
JavaScript
dog.test.js
1import "@testing-library/jest-dom";
2import { render, screen } from "@testing-library/react";
3import { MockedProvider } from "@apollo/client/testing";
4import { GET_DOG_QUERY, Dog } from "./dog";
5
6const mocks = [
7  {
8    request: {
9      query: GET_DOG_QUERY,
10      variables: {
11        name: "Buck"
12      }
13    },
14    result: {
15      data: {
16        dog: { id: "1", name