How to Mock React Query in React
Testing components that use React Query requires a strategy to handle asynchronous data fetching without making actual network requests. This article provides a straightforward guide on how to mock React Query in your React applications, focusing on the two most effective methods: mocking the API layer using Mock Service Worker (MSW) and mocking the React Query hooks directly with testing frameworks like Jest or Vitest.
Method 1: Mocking the Network Layer with MSW (Recommended)
The most robust way to mock React Query is to mock the network requests at the API layer using Mock Service Worker (MSW). This approach allows React Query to run naturally in your tests, ensuring that cache behavior, loading states, and error handling are fully exercised.
1. Set up the Custom Render Wrapper
To test React Query components, you must wrap them in a
QueryClientProvider. To avoid repeating this setup, create
a custom render function. It is important to turn off retries in your
test configuration to prevent tests from timing out when verifying error
states.
import React from 'react';
import { render } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const createTestQueryClient = () => new QueryClient({
defaultOptions: {
queries: {
retry: false, // Prevents test delays on failures
},
},
});
export function renderWithClient(ui) {
const testQueryClient = createTestQueryClient();
const { rerender, ...result } = render(
<QueryClientProvider client={testQueryClient}>{ui}</QueryClientProvider>
);
return {
...result,
rerender: (rerenderUi) =>
rerender(
<QueryClientProvider client={testQueryClient}>{rerenderUi}</QueryClientProvider>
),
};
}2. Define MSW Handlers and Write the Test
Define your mock API endpoints and write your test case using React Testing Library to wait for the data to appear on the screen.
import { screen, waitFor } from '@testing-library/react';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { renderWithClient } from './utils';
import { MyUserProfile } from './MyUserProfile';
const server = setupServer(
http.get('/api/user', () => {
return HttpResponse.json({ name: 'John Doe' });
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test('renders user profile successfully', async () => {
renderWithClient(<MyUserProfile />);
expect(screen.getByText(/loading/i)).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
});Method 2: Mocking the React Query Hook Directly
If you want to bypass the network layer entirely and only test your
UI’s presentation logic, you can mock the custom hook that wraps React
Query, or mock useQuery directly.
1. Mock a Custom Hook
If you wrap your React Query calls inside a custom hook, you can mock that hook using your testing framework’s mocking capabilities.
import { render, screen } from '@testing-library/react';
import { MyUserProfile } from './MyUserProfile';
import { useUserQuery } from './hooks/useUserQuery';
// Mock the hook file
jest.mock('./hooks/useUserQuery');
test('renders mocked user data', () => {
// Define mock return value
useUserQuery.mockReturnValue({
data: { name: 'Jane Smith' },
isLoading: false,
isError: false,
});
render(<MyUserProfile />);
expect(screen.getByText('Jane Smith')).toBeInTheDocument();
});2. Mock useQuery Directly
If you are calling useQuery directly within your
component, you can mock the @tanstack/react-query
package.
import { render, screen } from '@testing-library/react';
import { MyUserProfile } from './MyUserProfile';
import * as reactQuery from '@tanstack/react-query';
jest.mock('@tanstack/react-query', () => ({
...jest.requireActual('@tanstack/react-query'),
useQuery: jest.fn(),
}));
test('renders loading state and then data', () => {
const useQueryMock = jest.spyOn(reactQuery, 'useQuery');
// Mock loading state
useQueryMock.mockReturnValue({
isLoading: true,
data: undefined,
});
const { rerender } = render(<MyUserProfile />);
expect(screen.getByText(/loading/i)).toBeInTheDocument();
// Mock success state
useQueryMock.mockReturnValue({
isLoading: false,
data: { name: 'Alice' },
});
rerender(<MyUserProfile />);
expect(screen.getByText('Alice')).toBeInTheDocument();
});