2

I have looked at the following posts and am still running into an issue with following redirects with useNavigate in React.

react jest mock useNavigate()

https://github.com/remix-run/react-router/issues/7811

Create Post Snippet

const result = await dispatch(postCreateView(postObj))
if(result){
     nav('/posts')
}

Test Setup

import {screen, render, fireEvent} from '@testing-library/react'
import { MockComponent } from '../../testMockComponentWrapper'
import { PostCreate } from '../PostCreate'

const mockedUsedNavigate = jest.fn();

jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
   useNavigate: () => mockedUsedNavigate,
 }));

describe('Post Create', () => {

    it('successful post with redirect', async () => {
        const {container} = render(<MockComponent><PostCreate/></MockComponent>)
        const titleElement = screen.getByPlaceholderText(/enter title/i)
        const descriptionElement = screen.getByPlaceholderText(/enter description/i)
        const buttonElement = screen.getByRole('button')

        fireEvent.change(titleElement, {target: {value: 'A New title from Testing'}})
        fireEvent.change(descriptionElement, {target: {value: 'A New description from Testing'}})
        fireEvent.click(buttonElement)
        
        expect(mockedUsedNavigate).toHaveBeenCalledTimes(1);
        
    })

})

MockedComponent:

export const MockComponent = (props) => {
    return(
        <BrowserRouter>
            <Provider store={store}>
                {props.children}
            </Provider>
        </BrowserRouter>
    )
}

I have also tried

export const MockComponent = (props) => {
    return(
        <Router>
            <Provider store={store}>
                {props.children}
            </Provider>
        </Router>
    )
}

BrowserRouter gives the error:

TypeError: Cannot read property 'pathname' of undefined

With Router I the Error I'm getting:

Expected number of calls: 1
Received number of calls: 0

I would have thought that the number of calls would have been 1 since useNavigate would have been handled by jest, but that doesn't seem to be the case.

Versions I'm using:

"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"react": "^17.0.2",
"react-router-dom": "^6.2.1",

Just an FYI, the following does work and tests the dispatch method for react-redux:

const mockDispatch = jest.fn();

jest.mock('react-redux', () => ({
    ...jest.requireActual('react-redux'),
    useDispatch: () => mockDispatch
}))

// In the test
await expect(mockDispatch).toHaveBeenCalledTimes(1);

I would appreciate anyone's assistance with this. If there's a better place to post this question or SO guidelines I have missed, I apologize in advance and guidance would be appreciated. Thank you in advance.

Linus
  • 411
  • 5
  • 14

1 Answers1

5

Arg! Super simple answer...sometimes it helps to post a question on SO so it forces me to dig deeper... :)

Here's the solution and explanation for anyone interested...thanks for reading

import {screen, render, fireEvent} from '@testing-library/react'
import { MockComponent } from '../../testMockComponentWrapper'
import { PostCreate } from '../PostCreate'

// mock useDispatch
const mockDispatch = jest.fn();

jest.mock('react-redux', () => ({
    ...jest.requireActual('react-redux'),
    useDispatch: () => mockDispatch.mockReturnValueOnce(true) // Return a value since I'm expecting a value to be returned before I redirect
}))

// mock useNavigate
const mockedUsedNavigate = jest.fn();

jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
   useNavigate: () => mockedUsedNavigate, // Return an empty jest function to test whether it was called or not...I'm not depending on the results so no need to put in a return value
 }));



describe('Post Create', () => {

    it('successful post with redirect', async () => {
        const {container} = render(<MockComponent><PostCreate/></MockComponent>)
        const titleElement = screen.getByPlaceholderText(/enter title/i)
        const descriptionElement = screen.getByPlaceholderText(/enter description/i)
        const buttonElement = screen.getByRole('button')

        fireEvent.change(titleElement, {target: {value: 'A New title from Testing'}})
        fireEvent.change(descriptionElement, {target: {value: 'A New description from Testing'}})
        fireEvent.click(buttonElement)
        
        // Await the dispatch results
        await expect(mockDispatch).toHaveBeenCalledTimes(1);
        await expect(mockedUsedNavigate).toHaveBeenCalledTimes(1);
    })

})

If there are things that I missed in my explanation or understanding, updates or insights would be appreciated. Thank you,

Linus
  • 411
  • 5
  • 14
  • Thanks, it is helpful. By any chance, have you happened to mock any charts? I am using react-plotly.js and need to find a way to mock it. I will be posting a question soon, just wondering if you have it off hand. – GoWin May 24 '22 at 10:41
  • Sorry I haven't played around with mocking any charts just yet, but if you send me a link to your post with details I'll have a look. I'm just getting into charting and plotting myself :) – Linus May 31 '22 at 04:10