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")
})
})
Top comments (2)
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
How do you write unit tests without mocking ever? Surely if you have to include every dependency all the time you're going to end up writing integration tests no?