How to mock useHistory hook in jest?

前端 未结 5 534
無奈伤痛
無奈伤痛 2020-12-17 07:55

I am using UseHistory hook in react router v5.1.2 with typescript? When running unit test, I have got issue.

TypeError: Cannot read property \'history

相关标签:
5条回答
  • 2020-12-17 08:27

    This one worked for me:

    jest.mock('react-router-dom', () => ({
      ...jest.requireActual('react-router-dom'),
      useHistory: () => ({
        push: jest.fn()
      })
    }));
    
    0 讨论(0)
  • 2020-12-17 08:31

    Here's a more verbose example, taken from working test code (since I had difficulty implementing the code above):

    Component.js

      import { useHistory } from 'react-router-dom';
      ...
    
      const Component = () => {
          ...
          const history = useHistory();
          ...
          return (
              <>
                  <a className="selector" onClick={() => history.push('/whatever')}>Click me</a>
                  ...
              </>
          )
      });
    

    Component.test.js

      import { Router } from 'react-router-dom';
      import { act } from '@testing-library/react-hooks';
      import { mount } from 'enzyme';
      import Component from './Component';
      it('...', () => {
        const historyMock = { push: jest.fn(), location: {}, listen: jest.fn() };
        ...
        const wrapper = mount(
          <Router history={historyMock}>
            <Component isLoading={false} />
          </Router>,
        ).find('.selector').at(1);
    
        const { onClick } = wrapper.props();
        act(() => {
          onClick();
        });
    
        expect(historyMock.push.mock.calls[0][0]).toEqual('/whatever');
      });
    
    0 讨论(0)
  • 2020-12-17 08:32

    Wearing my politician hat I'll dare to state that you're asking the wrong question.

    It's not useHistory that you want to mock. Instead you'd just want to feed it with history object which you control.

    This also allows you to check for push invocations, just like the 2 top answers (as of writing this).

    If that's indeed the case, createMemoryHistory got your back:

    import {Router} from 'react-router-dom'
    import {createMemoryHistory} from 'history'
    
    test('QuestionContainer should handle navigation', () => {
      const history = createMemoryHistory()
      const pushSpy = jest.spyOn(history, 'push') // or 'replace', 'goBack', etc.
      render(
          <Router history={history}>
            <QuestionContainer/>
          </Router>
      )
      userEvent.click(screen.getByRole('button')) // or whatever action relevant to your UI
      expect(pushSpy).toHaveBeenCalled()
    })
    
    0 讨论(0)
  • 2020-12-17 08:33

    I needed the same when shallowing a react functional component that uses useHistory.

    Solved with the following mock in my test file:

    jest.mock('react-router-dom', () => ({
      useHistory: () => ({
        push: jest.fn(),
      }),
    }));
    
    0 讨论(0)
  • 2020-12-17 08:39

    In github react-router repo i found that useHistory hook uses singleton context, when i started use in mount MemoryRouter it found context and started works. So fix it

    import { MemoryRouter } from 'react-router-dom';
    const tree =  mount(<MemoryRouter><QuestionContainer {...props} /> </MemoryRouter>);
    
    0 讨论(0)
提交回复
热议问题