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.

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();
});