How to test RTK Query with Jest and RTL


This post goes over how to test RTK Query API with Jest and React Testing Library:

Prerequisites

Install and set up:

Enable jest-fetch-mock in setupTests:

// src/setupTests.ts
import fetchMock from 'jest-fetch-mock';

fetchMock.enableMocks();

We’ll be testing RTK Query’s createApi example. This means the auto-generated React hook is exported from api.ts:

// src/api.ts
export const { useGetPokemonByNameQuery } = pokemonApi;

Test

Create api.test.tsx:

touch api.test.tsx

Create an empty test:

it('renders hook', () => {
  // ...
});

Use renderHook to render the hook:

import { useGetPokemonByNameQuery } from './api';

it('renders hook', () => {
  renderHook(() => useGetPokemonByNameQuery('pikachu'));
});

The test should fail with the error:

Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>

Create a wrapper component using the Redux store:

// ...
import { store } from './store';
import { Provider } from 'react-redux';
import type { ReactNode } from 'react';

function Wrapper(props: { children: ReactNode }) {
  return <Provider store={store}>{props.children}</Provider>;
}

it('renders hook', () => {
  renderHook(() => useGetPokemonByNameQuery('pikachu'), { wrapper: Wrapper });
});

The test should now pass.

Mock the fetch call in beforeAll:

const data = {};

beforeAll(() => {
  fetchMock.mockOnceIf('https://pokeapi.co/api/v2/pokemon/pikachu', () =>
    Promise.resolve({
      status: 200,
      body: JSON.stringify({ data }),
    })
  );
});

Assert that the hook result is correct:

it('renders hook', () => {
  const { result } = renderHook(() => useGetPokemonByNameQuery('pikachu'), {
    wrapper: Wrapper,
  });

  expect(result.current).toMatchObject({
    status: 'pending',
    endpointName: 'getPokemonByName',
    isLoading: true,
    isSuccess: false,
    isError: false,
    isFetching: true,
  });
});

Assert that fetchMock is called:

it('renders hook', () => {
  // ...
  expect(fetchMock).toBeCalled();
});

But the test should fail. This is because we need to use waitFor to wait for the request to finish:

it('renders hook', async () => {
  // ...
  await waitFor(() => expect(result.current.isSuccess).toBe(true));
  expect(fetchMock).toBeCalledTimes(1);
});

The test should pass again.

Finally, assert that the updated hook result is correct:

it('renders hook', async () => {
  // ...
  expect(result.current).toMatchObject({
    status: 'fulfilled',
    endpointName: 'getPokemonByName',
    data: {},
    isLoading: false,
    isSuccess: true,
    isError: false,
    currentData: {},
    isFetching: false,
  });
});

Demo

See CodeSandbox.



Please support this site and join our Discord!