简体   繁体   中英

Mocking react-router-dom useHistory and other hooks with jest

I have a CRA and Typescript (.tsx) application. I'm trying to write a test for a component that needs useHistory and useRouteMatch but it return the error: TypeError: Cannot read property 'history' of undefined .

The component:

const SidebarTreeNav: React.FC<Props> = ( { tree, expanded } ) => {
  const history = useHistory();
  let { url } = useRouteMatch();

  const handleClick = useCallback(
    ( path: string ) => history.push(`${ url }/${ path }`),
    [ history ]);

  return (
    <SidebarTreeNavView expanded={ expanded ?? [ '0' ] }>
      <SidebarTreeNavBlock handleClick={ handleClick } tree={ tree } node={ 0 } />
    </SidebarTreeNavView>
  );
};

export default SidebarTreeNav;

The Test:

  beforeEach(() => {
    jest.mock('react-router-dom', () => {
      const originalModule = jest.requireActual('react-router-dom');

      return {
        __esModule: true,
        ...originalModule,
        useRouteMatch: { url: '/entry' },
        useHistory: jest.fn(),
      };
    });

    shallow = createShallow();
    wrapper = shallow(<SidebarTreeNav tree={ [] } />);
  });

  it('<SidebarTreeNav /> should be defined', () => {
    expect(wrapper).toBeDefined();
  });

Probably this answer is not needed for you anymore, but I'll post it for future reference.

First, you defined jest.mock in the wrong place, it has to be static, not mocked before every component mount - that solves your useHistory error. Second, you mocked useRouteMatch in a wrong way, it would've thrown that useRouteMatch is not a function. Third, why do you need that __esModule: true ?

Anyways, here's my working solution (I removed some parts that were not relevant):

SidebarTreeNav component:

import React, { useCallback } from 'react';
import { useHistory, useRouteMatch } from 'react-router-dom';

const SidebarTreeNav = () => {
  const history = useHistory();
  const { url } = useRouteMatch();

  const handleClick = useCallback(
    (path: string) => history.push(`${url}/${path}`),
    [history]
  );

  // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
  return <div onClick={() => handleClick(url)}>expanded</div>;
};

export default SidebarTreeNav;

test file:

import { shallow, ShallowWrapper } from 'enzyme';
import React from 'react';

import SidebarTreeNav from './SideBarTreeNav';

jest.mock('react-router-dom', () => {
  const originalModule = jest.requireActual('react-router-dom');

  return {
    ...originalModule,
    useHistory: jest.fn(),
    useRouteMatch: jest.fn(() => {
      return { url: '/entry' };
    }),
  };
});

describe('whatever', () => {
  let wrapper: ShallowWrapper;

  beforeEach(() => {
    wrapper = shallow(<Temporary />);
  });

  it('<SidebarTreeNav /> should be defined', () => {
    expect(wrapper).toBeDefined();
  });
});

A side note: it's really hard to work with your example because you posted just some part of your code, next time please add a fully replicated js fiddle.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM