How to mock useHistory hook in jest?
Solution 1
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(),
}),
}));
Solution 2
This one worked for me:
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: () => ({
push: jest.fn()
})
}));
Solution 3
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');
});
Solution 4
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>);
Solution 5
I found the above answers very helpful. However I missed the ability to spy and actually test functionality. But simply naming the mock function first solved that for me.
const mockPush = jest.fn();
jest.mock('react-router-dom', () => ({
useHistory: () => {
const push = () => mockPush ();
return { push };
},
}));
Ivan Martinyuk
Updated on February 14, 2022Comments
-
Ivan Martinyuk about 2 years
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' of undefined.
import { mount } from 'enzyme'; import React from 'react'; import {Action} from 'history'; import * as router from 'react-router'; import { QuestionContainer } from './QuestionsContainer'; describe('My questions container', () => { beforeEach(() => { const historyHistory= { replace: jest.fn(), length: 0, location: { pathname: '', search: '', state: '', hash: '' }, action: 'REPLACE' as Action, push: jest.fn(), go: jest.fn(), goBack: jest.fn(), goForward: jest.fn(), block: jest.fn(), listen: jest.fn(), createHref: jest.fn() };//fake object jest.spyOn(router, 'useHistory').mockImplementation(() =>historyHistory);// try to mock hook }); test('should match with snapshot', () => { const tree = mount(<QuestionContainer />); expect(tree).toMatchSnapshot(); }); });
Also i have tried use
jest.mock('react-router', () =>({ useHistory: jest.fn() }));
but it still does not work. -
Pnar Sbi Wer over 4 yearsthis approach preserves the other react-router-dom functions which you may not want to mock
-
Mukund Kumar about 4 years@Erhan i have done the same. but again it is throwing error : TypeError: Cannot read property 'history' of undefined. any suggestion ?
-
Hiroki almost 4 yearsFor those who use TypeScript, this approach may cause the "React.createElement: type is invalid — expected a string" error if the component uses
Link
anduseHistory
at the same time. Erhan's approach won't cause that issue. -
wentjun almost 4 yearsThis won't work in TypeScript, as it will give the following error:
TS2698: Spread types may only be created from object types.
-
taystack over 3 yearsIs there a way to capture
useHistory().push()
invocations? -
Jonathan Reyes over 3 yearsFor TypeScript support, see this answer.
-
omeralper over 3 yearsBut how do you spyOn useHistory function?
-
wentjun over 3 yearsThose who are using TypeScript may refer to this: stackoverflow.com/q/62774929/10959940 :)
-
Amit Kumar over 3 years@proustibat, can you provide a bit elaborated example ? Also, update the example with .test.js file
-
Amit Kumar over 3 yearsPlease let us know how we will get ...props value ??
-
targumon over 2 years@taystack you may want to check the answer I posted for this question ^_^
-
targumon over 2 years@omeralper you may want to check the answer I posted for this question ^_^
-
taystack over 2 years@targumon I solved this by scoping the jest.fn() outside the mock. It can be referenced easily this way. Mocking the router has been abstracted into a helper method already.
-
targumon over 2 yearsWhy so cumbersome? This should be written like this:
useHistory: () => ({ push: mockPush })