Testing navigate()
is slightly more problematic with the latest v6 (as of writing this post) react-router than just asserting on history.push()
as it was the case in the previous versions. Let’s say we have this ButtonHome
component:
import { useNavigate } from 'react-router-dom'
const ButtonHome = () => {
const navigate = useNavigate()
const onClick = () => navigate('/home')
return (
<button onClick={onClick}>
Home
</button>
)
}
I would write a test for this component using the react-testing-library in the following way:
import * as router from 'react-router'
import { render } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import ButtonHome from './ButtonHome'
describe('ButtonHome', () => {
const ui = userEvent.setup()
const navigate = jest.fn()
beforeEach(() => {
jest.spyOn(router, 'useNavigate').mockImplementation(() => navigate)
})
it('renders the button and navigates to /home upon click', async () => {
render(withRouter(<ButtonHome />))
await ui.click(screen.queryByText('Home'))
expect(navigate).toHaveBeenCalledWith('/home')
})
})
The relevant bits just for testing the router are as follows:
import * as router from 'react-router'
const navigate = jest.fn()
beforeEach(() => {
jest.spyOn(router, 'useNavigate').mockImplementation(() => navigate)
})
it('...', () => {
expect(navigate).toHaveBeenCalledWith('/path')
})
The test also requires the following withRouter()
helper, which I have in jest.setup.js
:
import { Route, Router, Routes } from 'react-router-dom'
import { createBrowserHistory } from 'history'
const history = createBrowserHistory()
const withRouter = (children, opts = {}) => {
const { path, route } = opts
if (path) {
history.push(path)
}
return (
<Router location={history.location} navigator={history}>
<Routes>
<Route
path={route || path || '/'}
element={children}
/>
</Routes>
</Router>
)
}
global.withRouter = withRouter