6

In my component that I want to test I use useLocation hook. In the component I have:

function Test() {
  const history = useHistory();
  const location = useLocation();
  const query = new URLSearchParams(location.search).get('value');

  return <div > Welcome < /div>
}

To test the component I wrote this test:

jest.mock('react-router-dom', () => ({
    useLocation: jest.fn().mockReturnValue({
        pathname: '',
        search: 'value',
        hash: '',
        state: null,
        key: '5nvxpbdafa',
    }),
}));

jest.mock('react-router-dom', () => ({
    useHistory: () => jest.fn().mockReturnValue({
        push: jest.fn(),
    }),
}));

describe(' page test', () => {
    test('should render', () => {
        render(<Test />);
        const title = screen.getByText(/welcome/i);

        expect(title).toBeInTheDocument();
    });
})

Trying this I get TypeError: (0 , _reactRouterDom.useLocation) is not a function. Why do I get this error? How to write the test correctly to avoid the error?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Asking
  • 3,487
  • 11
  • 51
  • 106
  • You're mocking it twice with two different implementations, only one of which has a `useLocation`. But you shouldn't be mocking it at all, you don't own that interface. – jonrsharpe Jul 04 '21 at 19:43
  • @jonrsharpe, i changed, but also i have in my code this: `const urlQuery = new URLSearchParams(location.search).get('value');`, and when i run this test i get `TypeError: Cannot read property 'search' of undefined`. Could you help please? How to solve this? – Asking Jul 04 '21 at 20:01
  • You could give a [mre] of that, but you'd be better off reading https://reactrouter.com/web/guides/testing. Again _don't mock what you don't own_, see also e.g. https://stackoverflow.com/a/65275037/3001761. – jonrsharpe Jul 04 '21 at 20:03
  • @jonrsharpe, i added some data in the question. Could you help? – Asking Jul 04 '21 at 20:08
  • Please re-read [mre]. Don't just post random snippets. – jonrsharpe Jul 04 '21 at 20:09
  • @jonrsharpe, my question was why i get the error if i set `search: 'value',`? – Asking Jul 04 '21 at 20:13
  • @jonrsharpe, i added my component. – Asking Jul 04 '21 at 20:20
  • Still the old mock, though. And still the wrong approach. Also stop posting snippets that aren't actually runnable. – jonrsharpe Jul 04 '21 at 20:24

2 Answers2

26

You'd better don't mock react-reouter-dom and the implementation of useLocation hook. Instead, you should use MemoryRouter wrap your component with initialEntries for testing:

An array of locations in the history stack. These may be full-blown location objects with { pathname, search, hash, state } or simple string URLs.

E.g. index.tsx:

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

export function Test() {
  const location = useLocation();
  const query = new URLSearchParams(location.search).get('value');
  console.log('query: ', query);

  return <div> Welcome </div>;
}

index.test.tsx:

import { render } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { Test } from './';

describe('68248240', () => {
  it('should pass', () => {
    render(
      <MemoryRouter initialEntries={[{ pathname: '/', search: '?value=teresa_teng' }]}>
        <Test />
      </MemoryRouter>
    );
  });
});

result:

 PASS  examples/68248240/index.test.tsx (10.807 s)
  68248240
    ✓ should pass (34 ms)

  console.log
    query:  teresa_teng

      at Test (examples/68248240/index.tsx:7:11)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        11.903 s
Lin Du
  • 88,126
  • 95
  • 281
  • 483
-1
  <Provider store={mockStore}>
    <MemoryRouter initialEntries={[{ pathname: '/Admin/NotificationPreferences' }]}>
      <NotificationPreferences />
    </MemoryRouter>
  </Provider

This is how I implemented and it worked for me.

Manik Kumar
  • 764
  • 7
  • 17