How to Mock your React GraphQL Tests with Relay

At Reckonsys, we use tools like React Testing Library to write unit test cases for our React applications. For one of our projects, we were having difficulty running the unit test cases because the components we were testing were making GraphQL API calls. Initially, we thought of running the backend server whenever we are running the test cases, but we wanted to run the test cases in CI using GitHub actions to streamline our workflow. We didn’t want to run a separate backend server just to run our test cases in CI, as this will incur additional costs. That’s when we explored other ways to handle this problem. We learned that we can mock the GraphQL calls in the test cases themselves. Using this method we won’t need to run a dedicated backend server to run the test cases.

We will explore how to mock GraphQL API calls when running unit test cases. In this article, we will be using the relay compiler to handle GraphQL, and we will be using React Testing Library to write unit tests in React.

Prerequisites

Before proceeding with this tutorial, you should have a basic understanding of the following:

1. Setting up a simple ReactJS application

Let’s start with setting up a simple ReactJS application.

Open your terminal and run the following command to create a simple ReactJS application.

npx create-react-app mock_graphql

Next, we need to add the necessary packages to the project to run relay and the test cases. Run the following command to install all the necessary packages

2. Adding a GraphQL schema to the project

add this GraphQL schema file to the root of your application. The GraphQL schema will look like the following

type Query {
  hello: String
  me: User
}

type Mutation {
  post(data: String!): User!
}

type User {
  id: ID
  name: String
}

We have 2 queries and 1 mutation in this GraphQL schema. We will be concentrating on the query in this tutorial but the code will work for both mutations and queries. Refer to this link if you want to learn more about Queries and Mutations in GraphQL.

3. Adding relay to package.json

Next, we need to specify the location of the GraphQL schema file in the package.json file. Open your package.json file and add a section called relay in it and then add the following code

"relay": {
    "src": "./src/",
    "schema": "./schema.graphql",
    "language": "javascript"
}

Here,

src  – is the location of our source code

schema –  is the path to the schema file that we just added, and

language –  is the language that we are going to use

Next, add a new entry in the scripts section in the package.json so that we can compile our GraphQL queries. Add the following in the scripts section.

"r": "relay-compiler"

4. Adding helper functions

Let’s create a folder called relay inside the /src folder, and add a new file called index.js. Paste the following code inside it.

The network function is where we set up our fetch method which makes API calls to the backend.

Then we set up the environment object by passing the network as a parameter along with the GraphQL store. This is standard relay syntax.

Last we used fetchQuery here to make GraphQL calls. You will notice that the fetchQuery function is accepting a parameter called isTestEnvironment. Through this parameter, we will be passing our test environment object. We will go through it in detail later.

5. Adding GraphQL query & mutation

Inside the relay folder, create a folder called GraphQL, and inside that folder create two more folders called query and mutation.

Add a file called Hello.js inside the query folder, and paste the following code into it

import graphql from 'babel-plugin-relay/macro';

export const HelloQuery = graphql`
  query HelloQuery {
    hello
  }
`;

This query is self-explanatory. By looking at the GraphQL schema we previously added, we know that the “hello” query returns a single string.

Add a file called Post.js inside the mutation folder, and paste the following code into it

import graphql from 'babel-plugin-relay/macro';

export const PostMutation = graphql`
  mutation PostMutation($input: String!) {
    post(data: $input) {
      id
      name
    }
  }
`;

If you are familiar with relay you will understand why we need to create these folders. Relay has a convention that forces us to name our query and mutation based on the file and folder name it is present in. However, if we put the query and mutations inside a folder called graphql it becomes easier to write and understand the queries.

6. Adding a React component that we can use to test:

We will add a simple react component that will trigger a GraphQL API call. We can use this component for testing. Create a file called Testing.js under the /src. Paste the following code into it.

We are using the GraphQL query and the fetch method here that we added previously. This component makes a GraphQL call and prints whatever the backend sends on the screen.

7. Adding some testing utility functions:

Create a folder called testUtils under the /src folder and add a file called test-utils.js. Paste the following into it.

I will explain what this file is used for at the end of this tutorial. But as of now, all you need to know is that the helper function customRender will use the render method that @testing-library/react gives us to wrap any component with the Provider component.

8. Adding the test script:

Finally, we can add our test script to test the component. Create a file called mock.test.js under /src folder. Paste the following code into it

In order to mock the GraphQL calls triggered inside a component we need the following things

  1. We need to set up a test environment object for relay. We have used createMockEnvironment method to create an environment.
  2. We will use the customRender function to render the component. We have to use the test environment when we are making a GraphQL call. In this case, we are passing the test environment that we create as props into the Testing component. If you go back to the Testing component you will notice that we are passing the test environment to the fetchQuery method, so that the fetchQuery method will use the test environment when we are running the test case. For reference, I have captured the code once.

3. Next we can use the getAllOperations method to get all the initial GraphQL calls that Testing component makes.

4. In order to mock all the GraphQL operations we have to wrap the mocking code with ReactTestRenderer.act function.

5. Then for each operation we use environment.mock.resolve method which in turn uses MockPayloadGenerator to mock the data that will be sent to the component.

To use MockPayloadGenerator we can pass the mock data for each type of data or for each GraphQL type.

In this case, the String is a data type in GraphQL, here we have passed the following into the MockPayloadGenerator

String() {
  return "Sample";
}

This means that MockPayloadGenerator will use this to resolve all the strings that the GraphQL query needs. In this example, if you refer to our schema file the hello query will resolve into a string

type Query {
  hello: String <-- hello query will return a string
  me: User
}

So when you run the test case, the Testing component will get the text “Sample” through the API call which it will then print on the screen. Use the following command to run the test case

npm run test ./src/mock.test.js

If all goes well, you will see the following in your terminal

I have put a console log inside the Testing component to print out the response that we get from the GraphQL call. You will notice that in the first line, the console log has printed out the text “Sample”. This is the mock data that we sent through MockPayloadGenerator.

Further Steps:

This section explains some important points on how we can scale this setup in your own project.

  • In most modern React applications we will be using Redux or any other tool for stage management. The component will depend on the Store for the data. Do you remember the customRender function that we added? In the Provider component, we have the following code. Here instead of covering the {children} with React fragments, we can replace that with the Redux store provider. This will make the store accessible for any component that we are trying to test
const Provider = ({ children }) => {
  return <>
    {children}
  </>;
};
  • Remember that with this current example, we have only mocked the initial GraphQL calls that the component will make when the component renders on the page. This means that if you have events in your app that call more GraphQL APIs as the user interacts with the page you will need to write more mocking code for them in the test case.
  • In this tutorial, we have mocked the String GraphQL type with mock data. We also have the flexibility to mock entire GraphQL types, in this example we have User. You can use the following inside MockPayloadGenerator.generate to mock the User type. You can test this by calling me query that we added in this tutorial
MockPayloadGenerator.generate(operation, {
  User() {
    return {
        id: '121',
        name: 'Ember'
    }
  }
})

You can refer to the entire source code for the above in this repo.

129

You may also like

Leave a Reply

Your email address will not be published. Required fields are marked *