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.