How to Mock HashRouter in React

Testing React components that rely on HashRouter can be challenging because of how it interacts with the browser’s window location hash. This article provides a straightforward guide on how to mock or substitute HashRouter from react-router-dom using Jest and React Testing Library, ensuring your unit tests run reliably in a simulated environment.

The most effective way to “mock” HashRouter in a testing environment is to substitute it with MemoryRouter. Because HashRouter relies on browser history and hash changes, it can cause leaks and unexpected behavior between test runs. MemoryRouter keeps the history of your URL in memory, making it ideal for unit tests.

If your component does not hardcode the router itself, you can wrap your component under test in a MemoryRouter like this:

import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import MyComponent from './MyComponent';

test('renders component with routing', () => {
  render(
    <MemoryRouter initialEntries={['/home']}>
      <MyComponent />
    </MemoryRouter>
  );

  expect(screen.getByText(/home/i)).toBeInTheDocument();
});

Method 2: Mocking react-router-dom with Jest

If HashRouter is hardcoded inside the component you want to test and you cannot inject it from the outside, you must mock the react-router-dom module directly using Jest.

You can mock HashRouter so that it acts as a simple wrapper that renders its children without any of the routing side effects:

import { render, screen } from '@testing-library/react';
import App from './App'; // App contains <HashRouter> internally

// Mock react-router-dom
jest.mock('react-router-dom', () => {
  const originalModule = jest.requireActual('react-router-dom');
  return {
    ...originalModule,
    HashRouter: ({ children }) => <div data-testid="mock-hash-router">{children}</div>,
  };
});

test('renders App with mocked HashRouter', () => {
  render(<App />);
  expect(screen.getByTestId('mock-hash-router')).toBeInTheDocument();
});

Method 3: Spying on HashRouter

If you only want to mock the router for specific test suites, you can use jest.spyOn on the imported module. This keeps your other tests unaffected if they require the actual router implementation:

import * as reactRouter from 'react-router-dom';
import { render } from '@testing-library/react';
import App from './App';

test('spies and mocks HashRouter', () => {
  const hashRouterSpy = jest.spyOn(reactRouter, 'HashRouter')
    .mockImplementation(({ children }) => <div data-testid="spy-router">{children}</div>);

  render(<App />);
  
  expect(hashRouterSpy).toHaveBeenCalled();
  hashRouterSpy.mockRestore();
});