This is a premium alert message you can set from Layout! Get Now!

Mocking GraphQL requests using the React Apollo library

0

In this article, we’ll discuss how to test React components where the data and data changes within the component depend on a GraphQL API. Before we dive in, let’s quickly refresh our understanding of GraphQL and the React library we’ll use to interact with a GraphQL API, React Apollo.

Jump ahead:

GraphQL

GraphQL is a flexible and efficient way to interact with APIs, allowing clients to specify exactly what data they need and receive responses with the requested information. GraphQL can be used from both the perspective of a client, such as a frontend web application, or a server.

To retrieve data in GraphQL, queries are used. For example, the following query could be used to request a list of to-do items from a server:

query TodoList {
  todoList {
    id
    title
    description
  }
}

The query above would return a list of todo item objects with id, title, description, and date fields.

In addition to retrieving data, GraphQL also supports mutations to make changes to the server. A simple example of a mutation to delete a specific to-do item from a database might look like this:

mutation deleteTodo($id: ID!) {
  deleteTodo(id: $id) {
    id
  }
}

This mutation takes in the id of the to-do item to be deleted and returns the id of the deleted item when successful.

React Apollo library

Apollo Client, built by the Apollo GraphQL team, is a toolkit designed to make it easy for a client application to communicate with a GraphQL API. The React Apollo library offers a set of specific tools that can be integrated into React components.

useQuery()

One of the main functions provided by React Apollo to execute GraphQL queries is the useQuery() Hook. This Hook takes a GraphQL document as its first argument and returns a result object that includes the data, loading, and error statuses of the query request.

For example, consider the following TodoList component that uses the useQuery() Hook to request data from the TodoList query example we shared above:

import * as React from 'react';
import { useQuery } from "@apollo/react-hooks";

const TODO_LIST = `
  query TodoList {
    todoList {
      id
      title
      description
    }
  }
`;

export const TodoList = () => {
  const { loading, data, error } = useQuery(TODO_LIST);

  if (loading) {
    return <h2>Loading...</h2>
  }

  if (error) {
    return <h2>Uh oh. Something went wrong...</h2>
  }

  const todoItems = data.map((todo) => {
    return <li>{todo.title}</li>
  })

  return (
    <ul>{todoItems}</ul>
  )
}

In this example, the component displays a loading message while the query request is in progress, an error message if the request fails, and a list of to-do titles if and when the request is successful.

Attempting to render the above component in a unit test would likely result in failure due to a lack of context for the query or its results (loading, data, error, etc.). Because of this, we can use the @apollo/react-testing library to mock the GraphQL request. Mocking the request allows us to properly test the component without relying on a live connection to the API.

MockedProvider

The @apollo/react-testing library includes a utility called MockedProvider that allows us to create a mock version of the ApolloProvider component for testing purposes. The ApolloProvider component is a top-level component that wraps our React app and provides the Apollo client as context throughout the app.

MockedProvider enables us to specify the exact responses we want from our GraphQL requests in our tests, allowing us to mock these requests without actually making network requests to the API.

Let’s see how we can mock GraphQL requests in the loading, error, and success states.

Mocking GraphQL requests

Loading state

To mock GraphQL requests in the loading state, we can wrap our components with MockedProvider and provide an empty array as the value of the mocks prop:

import * as React from 'react';
import { render } from "@testing-library/react";
import { TodoList } from '../TodoList';

describe("<TodoList />", () => {
  it("renders the expected loading message when the query is loading", async () => {
    const {
      /* get query helpers*/
    } = render(
      <MockedProvider mocks={[]}>
        <TodoList></TodoList>
      </MockedProvider>
    );

    // assertions to test component under loading state
  });
});

Assuming we’re using Jest as the unit testing framework and react-testing-library as the testing utility, we can assert that the component renders the expected text in its markup:

import * as React from 'react';
import { render } from "@testing-library/react";
import { TodoList } from '../TodoList';

describe("<TodoList />", () => {
  it("renders the expected loading message when the query is loading", async () => {
    const { queryByText } = render(
      <MockedProvider mocks={[]}>
        <TodoList />
      </MockedProvider>
    );

    // assert the loading message is shown
    expect(queryByText('Loading...')).toBeVisible();
  });
});

Error state

By wrapping our components with MockedProvider and providing mock request and error (or errors) property values, we can simulate the error state by specifying the exact error responses we want for our GraphQL request:

import * as React from 'react';
import { render } from "@testing-library/react";
import { TodoList } from '../TodoList';

const TODO_LIST = `
  query TodoList {
    todoList {
      id
      title
      description
    }
  }
`;

describe("<TodoList />", () => {
  // ...

  it('renders the expected error state', async () => {
    const todoListMock = {
      request: {
        query: TODO_LIST,
      },
      error: new Error('Network Error!'),
    };

    const { /* queries */ } = render(
      <MockedProvider mocks={[todoListMock]}>
        <TodoList></TodoList>
      </MockedProvider>,
    );
    // assertions to test component under error state
  });
});

We’ll have our unit test assert that the <TodoList /> component correctly displays the expected error message when the TodoList GraphQL query has failed with a network error:

import * as React from 'react';
import { render } from "@testing-library/react";
import { TodoList } from '../TodoList';

const TODO_LIST = `
  query TodoList {
    todoList {
      id
      title
      description
    }
  }
`;

describe("<TodoList />", () => {
  // ...

  it('renders the expected error state', async () => {
    const todoListMock = {
      request: {
        query: TODO_LIST,
      },
      error: new Error('Network Error!'),
    };

    const { queryByText } = render(
      <MockedProvider mocks={[todoListMock]}>
        <TodoList></TodoList>
      </MockedProvider>,
    );

    // assert the error message is shown
    expect(queryByText('Uh oh. Something went wrong...')).toBeVisible();
  });
});

Success state

Lastly, to test GraphQL requests in a successful state, we can use the MockedProvider utility component to wrap our components and provide mock values for the request and result properties in the mock GraphQL object. The result property is used to simulate the expected successful outcome of the GraphQL request in our test:

import * as React from 'react';
import { render } from "@testing-library/react";
import { TodoList } from '../TodoList';

const TODO_LIST = `
  query TodoList {
    todoList {
      id
      title
      description
    }
  }
`;

describe("<TodoList />", () => {

  // ...

  // ...

  it('renders the expected UI when data is available', async () => {
    const todoListMock = {
      request: {
        query: TODO_LIST,
      },
      result: {
        data: {
          todos: [
            { 
              id: '1',
              title: 'Todo Item #1',
              description: 'Description for Todo Item #1',
            },
            { 
              id: '2',
              title: 'Todo Item #2',
              description: 'Description for Todo Item #2',
            }
          ]
        },
      },
    };

    const { /* queries */ } = render(
      <MockedProvider mocks={[todoListMock]}>
        <TodoList></TodoList>
      </MockedProvider>,
    );

    // assertions
    // ...
  });
});

GraphQL API requests are asynchronous, meaning that we often need to specify a waiting period in our tests before making assertions about the request’s outcome. React Testing Library provides the waitFor utility to handle this scenario.

waitFor can be used in a unit test when mocking an API call and waiting for the mock promises to resolve. An example of using waitFor in the above unit test would be:

import * as React from 'react';
import { render, waitFor } from "@testing-library/react";
import { TodoList } from '../TodoList';

const TODO_LIST = `
  query TodoList {
    todoList {
      id
      title
      description
    }
  }
`;

describe("<TodoList />", () => {

  // ...

  // ...

  it('renders the expected UI when data is available', async () => {
    const todoListMock = {
      request: {
        query: TODO_LIST,
      },
      result: {
        data: {
          todos: [
            { 
              id: '1',
              title: 'Todo Item #1',
              description: 'Description for Todo Item #1',
            },
            { 
              id: '2',
              title: 'Todo Item #2',
              description: 'Description for Todo Item #2',
            }
          ]
        },
      },
    };

    const { queryByText } = render(
      <MockedProvider mocks={[todoListMock]}>
        <TodoList></TodoList>
      </MockedProvider>,
    );

    // use the waitFor utility to wait for API request to resolve
    await waitFor(() => {
      // assert the title for each todo item is visible
      expect(queryByText('Todo Item #1')).toBeVisible();
      expect(queryByText('Todo Item #2')).toBeVisible();
    });
  });
});

Conclusion

Unit testing is a crucial aspect of software development. It verifies that our code is doing what we expect it to do and ensures that it works correctly with other code it interacts with.

When testing React components that communicate with an API, it’s important to avoid making actual API requests in our unit tests to save time and resources. Mocking GraphQL API requests, as described in this article, allows us to efficiently test our React components’ behavior without the added overhead of actual API requests.

The post Mocking GraphQL requests using the React Apollo library appeared first on LogRocket Blog.



from LogRocket Blog https://ift.tt/51keFf9
Gain $200 in a week
via Read more

Post a Comment

0 Comments
* Please Don't Spam Here. All the Comments are Reviewed by Admin.
Post a Comment

Search This Blog

To Top