How to Test useLocation Hook in React
Testing the useLocation hook from React Router is
essential for ensuring your components correctly respond to URL and
state changes. This article provides a straightforward guide on how to
test components utilizing useLocation using Jest and React
Testing Library. We will cover the two best approaches: wrapping your
component with MemoryRouter for realistic integration
testing, and mocking the hook directly for isolated unit tests.
Method 1: Using MemoryRouter (Recommended)
The most reliable way to test useLocation is by wrapping
your component in a MemoryRouter. This provider from
react-router-dom allows you to simulate different URL paths
and location states without needing a real browser environment.
The Component under Test
Consider this component that displays the current pathname and a custom state value passed through the navigation:
import React from 'react';
import { useLocation } from 'react-router-dom';
export default function LocationDisplay() {
const location = useLocation();
return (
<div>
<p data-testid="pathname">{location.pathname}</p>
<p data-testid="state-value">{location.state?.from || 'No State'}</p>
</div>
);
}The Test Case
Using MemoryRouter, you can define
initialEntries to mock the history stack, including the
pathname and location state:
import React from 'react';
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import LocationDisplay from './LocationDisplay';
test('renders pathname and state from useLocation', () => {
render(
<MemoryRouter initialEntries={[{ pathname: '/dashboard', state: { from: 'login' } }]}>
<LocationDisplay />
</MemoryRouter>
);
expect(screen.getByTestId('pathname')).toHaveTextContent('/dashboard');
expect(screen.getByTestId('state-value')).toHaveTextContent('login');
});Method 2: Mocking useLocation with Jest
If you want to isolate your component completely and avoid importing
MemoryRouter, you can mock the useLocation
hook directly using Jest.
The Test Case
We use jest.mock to intercept
react-router-dom and define what useLocation
should return:
import React from 'react';
import { render, screen } from '@testing-library/react';
import LocationDisplay from './LocationDisplay';
// Mock react-router-dom
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: () => ({
pathname: '/profile',
state: { from: 'homepage' }
})
}));
test('renders mocked pathname and state', () => {
render(<LocationDisplay />);
expect(screen.getByTestId('pathname')).toHaveTextContent('/profile');
expect(screen.getByTestId('state-value')).toHaveTextContent('homepage');
});Using MemoryRouter is generally preferred as it closely
mirrors real-world application behavior and avoids fragile mock setups.
However, mock overrides are useful when you need to test multiple
complex routing states in a single test file.