Testing GraphQL Queries with Apollo Client

In this blog post, we are going to learn how to test our GraphQL operations on an Apollo client in a React application.

Quality means doing it right when no one is looking! — Henry Ford

Mocked Provider

The Apollo MockedProvider is used to wrap the components tree for testing. This is similar to how we use the Provider to wrap the entire application at the root component level.

  • The MockedProvider makes the Apollo client available on React’s context.
  • It enables to define mock responses for individual queries for test.
  • Therefore, tests don’t need to interact with a real GraphQL server, instead we mock all the GraphQL data.
export const SPEAKERSQUERY = gql`
query speakers {
speakers {
id
name
bio
sessions {
id
title
}
featured
}
}
`;

Let’s create a file called Speakers.test.js, to write our test code.

Install Packages

To test our GraphQL application, we will first need to install some testing packages. In our example, I am using React DOM test utils and Enzyme to write our tests. Make sure to install the appropriate packages, before beginning the tests. The tests can also be written with Jest or other testing libraries.

Imports

import { MockedProvider } from "@apollo/client/testing";
import { mount } from "enzyme";
import { Speakers, SPEAKERSQUERY } from "./Speakers";
import React from "react";
import { act } from "react-dom/test-utils";
import wait from "waait";

Mock data for query

Our first step to write tests here, is to define mock data for our GraphQL query. Below is the mock data, for our speakers query.

The mock data, needs to match the query and the response exactly. The mock data cannot have missing parameters.

To keep it simple, I have just mocked one speaker, and a few sessions for the speaker. You can always add more speakers and sessions to this mock response for more thorough testing.

const mockSpeakerData = {
request: {
query: SPEAKERSQUERY,
},
result: {
data: {
speakers: [
{
id: "1234",
name: "Adhithi Ravichandran",
bio: "Here is my bio",
sessions: [
{
id: "1233",
title: "GraphQL Testing",
},
{
id: "2345",
title: "GraphQL Big picture",
},
],
featured: false,
},
],
},
},
};

The mock data contains a request and a result section. We can use the query parameter, and pass the SPEAKERSQUERY that we defined to the request. To the result, we pass the exact response that is expected from the GraphQL query.

Test Happy Path

We are ready to create our first test. In this test, we will pass the mock data and validate that the React component is rendered as expected.

it("renders speaker data", async () => {
let wrapper;
await act(async () => {
wrapper = mount(
<MockedProvider addTypename={false} mocks={[mockSpeakerData]}>
<Speakers />
</MockedProvider>
);
});

await act(() => wait(0));
wrapper.update();
expect(wrapper).toBeTruthy();
expect(wrapper.find(".panel-heading")).toHaveText(
"Speaker: Adhithi Ravichandran"
);
expect(wrapper.find(".panel-body")).toHaveText(
"Bio: Here is my bio"
);
});

We are using the mount method, that remounts a component. This comes with Enzyme package.

MockedProvider parameters:

The Speakers component is wrapped within the MockedProvider as shown above. In addition, the MockedProvider takes in some parameters as well as follows:

addTypeName: set this to false. This prevents Apollo Client from automatically adding a special typename field to every object.

mocks: Accepts the the mock data, for instance, mockSpeakerData.

wait()

We can introduce a wait, which waits for our component to update. It returns a promise that resolves after how many milliseconds you pass it.

It is perfect, for waiting any amount of time. If you do not pass it any value, it will immediately resolve.

Assertions

Once the GraphQL query operation is finished, we can assert the results. In our test, I have added expectations to find the speaker name and bio that was passed in the mocked data.

One the test is written, you can run npm test to run the test we just wrote.

Test Loading Speakers

Before the speaker data is returned as a GraphQL response, we will see a loading message on our UI. We can write a test for this loading scenario as well as follows.

it("renders loading speaker", () => {
let wrapper;
act(() => {
wrapper = mount(
<MockedProvider addTypename={false} mocks={[mockSpeakerData]}>
<Speakers />
</MockedProvider>
);
});

expect(wrapper).toBeTruthy();
expect(wrapper).toHaveText("Loading speakers...");
});

Notice we have removed the async/await method while making the call in this test . Since this call is going to be synchronous, the speaker data will not be returned yet, and we can expect to see the loading message instead.

Test Errors

The last test we are going to write, is one to test the error state. What if our GraphQL response returned an error? We can run into network errors or GraphQL specific errors while we try to retrieve data. To test this scenario, we will create a mock data with an error as shown below.

const mockErrorData = {
request: {
query: SPEAKERSQUERY,
},
error: new Error("Network Error"),
};

Now, we can pass this mockErrorData to the MockedProvider in our test.

it("renders with error", async () => {
let wrapper;
await act(async () => {
wrapper = mount(
<MockedProvider addTypename={false} mocks={[mockErrorData]}>
<Speakers />
</MockedProvider>
);
});

await act(() => wait(0));
expect(wrapper).toBeTruthy();
expect(wrapper).toHaveText("Error loading speakers!");
});

In this example, we have written a test to simulate network errors. Similarly, we can also write tests to validate GraphQL errors.

const mockGraphQLErrorData = {
request: {
query: SPEAKERSQUERY,
},
result: {
errors: [new GraphQLError('Error!')],
data: {
// ....
}
},
};

Resources and Talk

You can find all of the code for the app and the tests that we discussed in my repo below in the Tests branch:

https://github.com/adhithiravi/Consuming-GraphqL-Apollo/tree/Tests

You can also watch my talk below at the GraphQL Summit, which details everything we learned in this blog post:

You can also check out my courses on Pluralsight on GraphQL, linked below:

In conclusion, testing is an important piece of software development, that is often overlooked. Let’s make sure that we focus and prioritize testing in our projects.

In the next article we can learn about testing GraphQL mutations. I hope you enjoyed this article and talk. If you liked this post, don’t forget to share it with your network. You can follow me on twitter @AdhithiRavi for more updates.

Software Consultant, Author, Speaker, React Native|React|GraphQL Dev & Indian Classical Musician http://adhithiravichandran.com/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store