DEV Community

Teerasak Vichadee
Teerasak Vichadee

Posted on

Test `history` in React Router

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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")
  })
})
Enter fullscreen mode Exit fullscreen mode

Discussion (1)

Collapse
macnick profile image
Nick Haralampopoulos

Mocking is a code smell. I will explain why.
If, for some reason, history changes the method name from push to gotoPath 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