Jest:在函数组件中测试 useNavigate 钩子

Jest: Test useNavigate hook in function component

我正在用 Jest 编写测试,想测试一个简单的按钮,该按钮用作注销并通过 onClick 将您重定向到 "/login"

import {loginService} from "../../utils/loginService";
import React from "react";
import {useNavigate} from "react-router-dom";

const Logout = () => {
    const navigate = useNavigate()

    const logout = e => {
        loginService.logout();
        navigate("/login");
    }

    return (
        <button className={'logout'} onClick={logout}>Logout</button>
    )
}

export default Logout

我能够编写第一个测试来呈现按钮并检查它是否存在。第二个测试应该检查,点击浏览器将您重定向到“/login”。

import Logout from "../../components/navbar/Logout";
import {BrowserRouter} from "react-router-dom";
import {fireEvent, render} from "@testing-library/react";
import {screen} from '@testing-library/react';

describe('Test Logout Button', () => {
    it('it is rendered', () => {
        render(
            <BrowserRouter>
                <Logout/>
            </BrowserRouter>
        )
        expect(screen.getByText(/logout/i)).toBeInTheDocument()
    })

    it('it redirects after click', () => {
        const mockedUsedNavigate = jest.fn();

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

        render(
            <BrowserRouter>
                <Logout/>
            </BrowserRouter>
        )
        const logout = screen.getByText(/logout/i);

        fireEvent.click(logout);

        // TODO something something check useNavigate
        // https://blog.logrocket.com/testing-react-router-usenavigate-hook-react-testing-library/
        expect(mockedUsedNavigate).toBeCalledTimes(1);
    })
})

但是失败了:

expect(jest.fn()).toBeCalledTimes(expected)

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

我假设我必须以某种方式将模拟传递给我的组件。我该怎么做,或者我什至必须这样做?如何检查 useNavigate 是否已重定向到 "/login"?据我所知 <BrowserRouter /> 没有历史属性。

jest.mock 应该在您的单元测试之外完成,因为它需要在您要测试的代码对其进行评估之前模拟出一个依赖项。在您的示例中,react-router-dom 库首先由 Logout 组件评估,然后您的代码尝试模拟它,但为时已晚。因此,您应该将以下代码移到 describe:

之外
const mockedUsedNavigate = jest.fn();

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

当然,你所有的测试都将使用相同的 mockedUsedNavigate 实例,这很容易出错,所以你应该在每个单元测试之前通过将它添加到 beforeEach 来重置它,如下所示:

beforeEach(() => {
  mockedUsedNavigate.mockReset();
});