How to mock useLocation correctly?
Asked Answered
S

6

5

I have a component that uses useLocation hook to get the path from the URL.

const { pathname } = useLocation();
useEffect(() => { }, [pathname]);

While I am trying to mock the location using ,

import React from 'react';
import ExampleComponent from './ExampleComponent';
import { fireEvent, render } from '@testing-library/react';
import { shallow } from 'enzyme';

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useLocation: () => ({
    pathname: 'https://URL/'
  })
}));

describe('<ExampleComponent />', () => {
  it('should render correctly', () => {
    shallow(<ExampleComponent />);
  });
});

I am getting this error while I run the test, TypeError: Cannot read property 'location' of undefined

Segment answered 14/6, 2021 at 5:1 Comment(2)
I would suggest you don't mock it, mount the component in e.g. a MemoryRouter or create a history for testing purposes; see https://mcmap.net/q/271325/-recommended-approach-for-route-based-tests-within-routes-of-react-router, reactrouter.com/web/guides/testingChristiachristian
the correct way is to use MemoryRouter as below in the answer https://mcmap.net/q/1942044/-how-to-mock-uselocation-correctlyEisenstark
E
5

Below is how I have done this in my tests. * Note I am using typescript

import routeData from 'react-router';    

    describe('Login Page UnitTests', () => {
      const useLocation = jest.spyOn(routeData, 'useLocation');
    
      beforeEach(() => {
        useLocation.mockReturnValue({ search: 'testQueryParameters'} as any);
      });

      // Add unit tests
    }

Ensure that you clear the mock to avoid issue with data in subsequent tests

Emptyheaded answered 14/6, 2021 at 5:26 Comment(1)
+1 This is a much cleaner solution. I try to avoid any though, so I stubbed out the whole Location with useLocation.mockReturnValue({ pathname: '', search: '', state: {}, hash: '', })Thorsten
U
4

Try mocking the useLocation as jest.fn().mockImplementation

jest.mock('react-router', () => ({
...jest.requireActual("react-router") as {},
   useLocation: jest.fn().mockImplementation(() => {
    return { pathname: "/testroute" };
   })
}));
Uniaxial answered 14/6, 2021 at 5:14 Comment(1)
Worked fine perfectly, Thank youSegment
E
1

The correct way to mock useLocation is below:

import React from 'react';
import ExampleComponent from './ExampleComponent';
import { fireEvent, render } from '@testing-library/react';
import { MemoryRouter} from 'react-router-dom';
import { shallow } from 'enzyme';

const renderComponent = () => {
  return (
      <MemoryRouter
          initialEntries={["/one", "/two", { pathname: 'https://URL/' }]}
          initialIndex={1}>
         <ExampleComponent />
     </MemoryRouter>
   );
}

describe('<ExampleComponent />', () => {
  it('should render correctly', () => {
    shallow(renderComponent());
  });
});
Eisenstark answered 16/12, 2022 at 5:28 Comment(0)
M
0

This works for me

import * as router from 'react-router';

    jest
      .spyOn(router, 'useLocation')
      .mockReturnValue({ pathname: '/department/details/1213', search: '', state: {}, hash: '' });
Mohenjodaro answered 19/10, 2023 at 4:11 Comment(0)
S
0

I'll post something additional here where you want mock useLocation with state variables.

Code

Here I'm navigating from page1 to page2 and I want to pass a state variable to new route.

navigate('/another/page2', {state: {name: 'john'}});

Then inside page to I can read the location state.

const location = useLocation();
const locationState = location.state as { name?: string };

Test

I can mock useLocation with location state as below.

const mockUseLocation = {
    pathname: '/another/page2',
    key: '',
    search: '',
    hash: '',
    state: { name: 'mock name' },
};

jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
    useLocation: () => mockUseLocation,
}));

it('test1', async () => {
    // write your test here
});
Solingen answered 2/11, 2023 at 12:11 Comment(0)
S
0

Finally found the simplest way to mock useLocation:

import { useLocation } from 'react-router-dom';

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useLocation: jest.fn()
}));

const mockUseLocation = useLocation as jest.MockedFunction<typeof useLocation>;

Now you can mock the return value inside any test case. For example:

it('should render', () => {
    mockUseLocation.mockReturnValue({
      search: '?param=1'
    });

    // ...
});

If you're using typescript, you can set a const to define the default values of all the properties:

const defaultUseLocationResult = { pathname: '', search: '', state: {}, hash: '' };

and apply it as follows:

mockUseLocation.mockReturnValue({
    ...defaultUseLocationResult,
    search: '?param=1'
});
Scoter answered 8/2 at 13:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.