How to mock history.push with the new React Router Hooks using Jest
Solution 1
You actually do not need to mock react-router-dom
(at least for v5) as it provides a bunch of testing tools: https://v5.reactrouter.com/web/guides/testing
To check your history is actually changed, you can use createMemoryHistory
and inspect its content:
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Menu } from './Menu';
import { createMemoryHistory } from 'history'
import { Router } from 'react-router-dom';
test('triggers path change', () => {
const history = createMemoryHistory();
render(
<Router history={history}>
<Menu />
</Router>
);
const aboutItem = screen.getByText('About');
expect(aboutItem).toBeInTheDocument();
userEvent.click(aboutItem);
expect(history.length).toBe(2);
expect(history.location.pathname).toBe('/about');
});
Solution 2
Use jest.mock
in module scope will automatically be hoisted to the top of the code block. So that you can get the mocked version react-router-dom
in NotFound.jsx
file and your test file.
Besides, we only want to mock useHistory
hook, so we should use jest.requireActual()
to get the original module and keep other methods as the original version.
Here is the solution:
NotFound.jsx
:
import React from 'react';
import { useHistory } from 'react-router-dom';
const RouteNotFound = () => {
const history = useHistory();
return (
<div>
<button onClick={() => history.push('/help')} />
</div>
);
};
export default RouteNotFound;
NotFound.test.jsx
:
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { render, fireEvent } from '@testing-library/react';
import RouteNotFound from './NotFound';
const mockHistoryPush = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: () => ({
push: mockHistoryPush,
}),
}));
describe('RouteNotFound', () => {
it('Redirects to correct URL on click', () => {
const { getByRole } = render(
<MemoryRouter>
<RouteNotFound />
</MemoryRouter>,
);
fireEvent.click(getByRole('button'));
expect(mockHistoryPush).toHaveBeenCalledWith('/help');
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/58524183/NotFound.test.jsx
RouteNotFound
✓ Redirects to correct URL on click (66ms)
--------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
--------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
NotFound.jsx | 100 | 100 | 100 | 100 | |
--------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.133s, estimated 11s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58524183
Albert Alises
Software Engineer based in Barcelona with a background in Computational Biomedical Engineering. Currently working as an Engineering Lead at Oliva Health
Updated on July 09, 2022Comments
-
Albert Alises almost 2 years
I am trying to mock
history.push
inside the newuseHistory
hook onreact-router
and using@testing-library/react
. I just mocked the module like the first answer here: How to test components using new react router hooks?So I am doing:
//NotFound.js import * as React from 'react'; import { useHistory } from 'react-router-dom'; const RouteNotFound = () => { const history = useHistory(); return ( <div> <button onClick={() => history.push('/help')} /> </div> ); }; export default RouteNotFound;
//NotFound.test.js describe('RouteNotFound', () => { it('Redirects to correct URL on click', () => { const mockHistoryPush = jest.fn(); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useHistory: () => ({ push: mockHistoryPush, }), })); const { getByRole } = render( <MemoryRouter> <RouteNotFound /> </MemoryRouter> ); fireEvent.click(getByRole('button')); expect(mockHistoryPush).toHaveBeenCalledWith('/help'); }); })
But
mockHistoryPush
is not called... What am I doing wrong? -
Enigmatic about 4 years@slidershowp2 can you please help me in stackoverflow.com/questions/61928263/…
-
AliF50 over 3 yearsHey @slideshowp2, thanks for this answer. I was trying to do this but I had the
jest.mock
inside of thedescribe
block and it wasn't working. Do you know why this is the case? I found this thread but I still don't understand it. github.com/facebook/jest/issues/2582 -
minh.duc over 3 yearsYou saved me. Thank you very much
-
Joseph Freeman about 3 yearsI'm having the same issue expect my component using
RouteComponentProps
along with theuseHIstory()
hook. The hook returns undefined with the components inside the test. Is there a solution for mockinguseHistory()
with a component that usesRouteComponentProps
? -
Hubert almost 3 yearsThis should be the accepted answer. Mocking react-router can cause unnecessary complexity.
-
Pavlo Zhukov over 2 yearsThe link is changed. Please update if possible.
-
ChumiestBucket about 2 yearsany idea how in RR6?