Problem
When we use hooks useHistory
of react-router-dom
How to expect the history
are changes?
How to mock the history
?
Solution
Don't try to expect global window.location
or window.history
.
Just render test component as a children of <Router>
, it's require history
object just create a simple Javascript object with mock on specific function (depend on your needs)
example from React Router document :p
// HomeButton.jsx
import { useHistory } from "react-router-dom";
export function HomeButton() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button data-testid="button" type="button" onClick={handleClick}>
Go home
</button>
);
}
the test file
// HomeButton.test.jsx
import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { HomeButton } from './HomeButton';
describe('HomeButton', () => {
// the test might throw an error about required properties, it's depend on your component's dependencies.
const mockHistory = {
push: jest.fn(),
}
it('should go to home after click', () => {
await act(async () => {
render(
<Router history={mockHistory}>
<HomeButton />
</Router>
)
userEvent.click(screen.getByTestId('button'))
})
expect(mockHistory.push).toBeCalledTime(1)
expect(mockHistory.push).toBeCalledWith("/home")
})
})
Discussion (1)
Mocking is a code smell. I will explain why.
If, for some reason, history changes the method name from
push
togotoPath
the test will pass because the function is mocked but the code will be broken.Also the test does not test if it actually goes to the url
/home
, actually it tests if the mocked function is called with/home