Mocking react-router-dom hooks using jest is not working

17,710

Solution 1

This works for me to mock useParams and change values for each unit test within the same file:

import React from "react";
import { render } from "@testing-library/react";
import Router from "react-router-dom";
import Component from "./Component";

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

const createWrapper = () => {
 return render(<Cases />);
};

describe("Component Page", () => {
 describe("Rendering", () => {
   it("should render cases container", () => {
     jest.spyOn(Router, 'useParams').mockReturnValue({ id: '1234' })
     const wrapper = createWrapper();
     expect(wrapper).toMatchSnapshot();
   });

   it("should render details container", () => {
     jest.spyOn(Router, 'useParams').mockReturnValue({ id: '5678' })
     const wrapper = createWrapper();
     expect(wrapper).toMatchSnapshot();
   });
 });
});

Just declare useParams as jest.fn() outside describe() and then change its values in each unit test with jest.spyOn

Solution 2

I am not sure why, also couldn't find it in the docs of react-router library, but changing react-router-dom to react-router in both tests and implementation worked for me.

So it becomes something like this:

import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import React from 'react';
import Header from './header';
import { shallow } from 'enzyme';

Enzyme.configure({ adapter: new Adapter() });

describe('<Header />', () => {

  jest.mock('react-router', () => ({
    useParams: jest.fn().mockReturnValue({ id: '123' }),
  }));

  it('renders', () => {
    const wrapper = shallow(<Header />);
    expect(wrapper).toBeTruthy();
  });

});

Solution 3

I've had a similar problem, I solved it like this:

import { Route, Router } from "react-router-dom";
import { createMemoryHistory } from "history";

const renderWithRouter = (component) => {
  const history = createMemoryHistory({
    initialEntries: ["/part1/idValue1/part2/idValue2/part3"],
  });
  const Wrapper = ({ children }) => (
    <Router history={history}>
      <Route path="/part1/:id1/part2/:id2/part3">{children}</Route>
    </Router>
  );
  return {
    ...render(component, { wrapper: Wrapper }),
    history,
  };
};

describe("test", () => {
  it("test desc", async () => {
    const { getByText } = renderWithRouter(<MyComponent/>);
    expect(getByText("idValue1")).toBeTruthy();
  });
});

Solution 4

I tried this mock but it doesn't work to me. Error: Cannot read property 'match' of undefined. It seems the component is not inside a router so it cannot mock the match with params. It works to me:

import { MemoryRouter, Route } from 'react-router-dom';

const RenderWithRouter = ({ children }) => (
  <MemoryRouter initialEntries={['uri/Ineed']}>
    <Route path="route/Ineed/:paramId">{children}</Route>
  </MemoryRouter>
);
const tf = new TestFramework();
describe('<MyComponent />', () => {
  tf.init({ title: 'Some test' }, props =>
    shallow(
      <RenderWithRouter>
        <MyComponent {...props} />
      </RenderWithRouter>
    )
  );

  it('Some description', () => {
    const wrapper = tf.render().html();
    expect(wrapper).toContain('something');
  });
});
Share:
17,710
Maxime Côté
Author by

Maxime Côté

Updated on June 19, 2022

Comments

  • Maxime Côté
    Maxime Côté almost 2 years

    I'm using Enzyme's shallow method to test a component which uses the useParams hook to get an ID from the URL params.

    I'm trying to mock the useParams hook so that it does't call the actual method, but it doesn't work. I'm still getting TypeError: Cannot read property 'match' of undefined, so it calls the actual useParams, and not my mock.

    My component:

    import React from 'react';
    import { useParams } from 'react-router-dom';
    
    export default () => {
    
      const { id } = useParams();
    
      return <div>{id}</div>;
    
    };
    

    Test:

    import Enzyme from 'enzyme';
    import Adapter from 'enzyme-adapter-react-16';
    import React from 'react';
    import Header from './header';
    import { shallow } from 'enzyme';
    
    Enzyme.configure({ adapter: new Adapter() });
    
    describe('<Header />', () => {
    
      jest.mock('react-router-dom', () => ({
        useParams: jest.fn().mockReturnValue({ id: '123' }),
      }));
    
      it('renders', () => {
        const wrapper = shallow(<Header />);
        expect(wrapper).toBeTruthy();
      });
    
    });
    

    Thank you!

  • Minkesh Jain
    Minkesh Jain over 4 years
    Thanks @ivan, If someone knows the logic behind it please post it here.
  • Ivan
    Ivan over 4 years
    Well I think I found it: react-router-dom does not define hooks. Hooks are defined in react-router package. Therefore if you want to mock react router hooks, you will need to mock that package. See: github.com/ReactTraining/react-router/blob/master/packages/…
  • Roger Peixoto
    Roger Peixoto over 4 years
    Even when I import those hooks from react-router and mock the react-router package, it still gives em the same error Cannot read property 'match' of undefined
  • Benjamin Charais
    Benjamin Charais over 3 years
    In general providing steps that helped you come to this solution can be very useful to others if you their fix is not exactly the same as yours, that aside, nice clean answer.
  • Vity
    Vity over 3 years
    I forgot to mention, I'm using react-testing-library but I hope the router part at least is similar
  • Mahdiyeh
    Mahdiyeh almost 3 years
    That was exactly what I needed. Thank you!
  • Carl
    Carl over 2 years
    This is the only method that worked for me after migrating to v6 from @reach/router.
  • Dykotomee
    Dykotomee about 2 years
    If you're going to call jest.mock on react-router for the purpose of mocking useParams, it's probably a good idea to include everything else before the useParams mock by adding ...jest.requireActual('react-router'). This ensures that the rest of react-router doesn't get lost.