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 🐶
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):
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 astoBeInTheDocument
). 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
:
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:
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 🐶
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: