Reputation: 2081
"react-router-dom": "^6.0.0-alpha.5",
I have tried nearly everything.
I just want to mock this navigate()
call from the useNavigate()
hook. that's it. Simple. Nothing working.
No, i do not want to use Link
. useNavigate is used programmatically in other places as well and I want to mock them too
import React from 'react'
import { useNavigate } from "react-router-dom"
export const Detail = () => {
const navigate = useNavigate();
return (
<span onClick={() => navigate('/some/specific/route')}>
some Text
</span>
)
}
I have tried these:
jest.mock('react-router-dom', () => {
// Require the original module to not be mocked...
const originalModule = jest.requireActual('react-router-dom');
return {
__esModule: true,
...originalModule,
// add your noops here
useNavigate: jest.fn(() => 'bar')
};
});
import * as ReactRouterDom from "react-router-dom";
...
// cannot redefine property
Object.defineProperty(ReactRouterDom, 'useNavigate', {
configurable: true,
value: jest.fn(() => 'bar')
});
// doesnt work
jest.mock('react-router-dom', () => ({
useNavigate: jest.fn(() => jest.fn),
}))
// doesnt work
jest.spyOn(ReactRouterDom, 'useNavigate', 'get').mockReturnValue(jest.fn(() => jest.fn));
// doesnt work
jest.spyOn(ReactRouterDom, 'useNavigate').mockReturnValue(jest.fn(() => jest.fn));
// doesnt work
const mockedUsedNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom') as any,
useNavigate: () => mockedUsedNavigate,
}));
all of these either show "Cannot redefine Property 'useNavigate'"
, or that useNavigate() may be used only in the context of a <Router> component.
Seriously, any other import mock works fine.
What am I doing wrong?
MY MINIMUM RECREATED PROJECT: https://github.com/zacharytyhacz/useNavigateBug
Upvotes: 47
Views: 70030
Reputation: 4943
I'm using React testing Library. Per the documentation, I have a test utils file which exports a custom render method. The custom render method wraps the rendered component in mocked providers as well as a router. This enables me to do the following:
it('redirects when span is clicked', async () => {
const span = await screen.findByText('some text')
await userEvent.click(span)
await waitFor(() => expect(window.location.href).toContain('/some/sepcific/route'))
})
To be fair, it's a different approach than what the OP asked specifically about. However, I think it's worth mentioning.
Upvotes: 4
Reputation: 9624
Here's how I mock and assert on navigate()
calls in my component tests:
import * as router from 'react-router'
const navigate = jest.fn()
beforeEach(() => {
jest.spyOn(router, 'useNavigate').mockImplementation(() => navigate)
})
it('...', () => {
...
expect(navigate).toHaveBeenCalledWith('/path')
})
Upvotes: 37
Reputation: 2251
You should be able to spy on useNavigate
normally if you change your import from:
import { useNavigate } from "react-router-dom"
to
import { useNavigate } from "react-router"
Upvotes: 16
Reputation: 2675
I had a similar concern that was fixed with this issue from react router
I would suggest you change the mock as is:
// pay attention to write it at the top level of your file
const mockedUsedNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom') as any,
useNavigate: () => mockedUsedNavigate,
}));
// your describe/it/test blocks go down there
Upvotes: 102