React 模拟测试 useNavigate

Lin*_*nus 3 reactjs jestjs react-router-dom

我已经查看了以下帖子,但仍然遇到了在 React 中使用 useNavigate 进行以下重定向的问题。

反应笑话模拟 useNavigate()

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

创建帖子片段

const result = await dispatch(postCreateView(postObj))
if(result){
     nav('/posts')
}
Run Code Online (Sandbox Code Playgroud)

测试设置

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);
        
    })

})
Run Code Online (Sandbox Code Playgroud)

模拟组件:

export const MockComponent = (props) => {
    return(
        <BrowserRouter>
            <Provider store={store}>
                {props.children}
            </Provider>
        </BrowserRouter>
    )
}
Run Code Online (Sandbox Code Playgroud)

我也尝试过

export const MockComponent = (props) => {
    return(
        <Router>
            <Provider store={store}>
                {props.children}
            </Provider>
        </Router>
    )
}
Run Code Online (Sandbox Code Playgroud)

BrowserRouter 给出错误:

TypeError: Cannot read property 'pathname' of undefined
Run Code Online (Sandbox Code Playgroud)

使用 Router I 时出现的错误:

Expected number of calls: 1
Received number of calls: 0
Run Code Online (Sandbox Code Playgroud)

我本以为调用次数会是 1,因为 useNavigate 会由 jest 处理,但情况似乎并非如此。

我正在使用的版本:

"@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",
Run Code Online (Sandbox Code Playgroud)

仅供参考,以下内容确实有效并测试了react-redux的调度方法:

const mockDispatch = jest.fn();

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

// In the test
await expect(mockDispatch).toHaveBeenCalledTimes(1);
Run Code Online (Sandbox Code Playgroud)

我将不胜感激任何人对此的帮助。如果有更好的地方可以发布这个问题或我错过的指南,我提前表示歉意,并且将不胜感激。先感谢您。

Lin*_*nus 6

嗯!超级简单的答案...有时它有助于在 SO 上发布问题,所以它迫使我更深入地挖掘...:)

这是任何感兴趣的人的解决方案和解释...感谢阅读

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);
    })

})
Run Code Online (Sandbox Code Playgroud)

如果我在解释或理解中遗漏了一些内容,那么我们将不胜感激。谢谢你,