You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React 18登录组件单元测试:useNavigate调用验证失败问题

问题分析与解决方案

问题根源

你的测试用例未检测到useNavigate被调用,核心原因是**react-router-dom的mock时机错误**:你在describe块内部才mockuseNavigate,但此时Login组件已经在文件顶部完成导入,Node.js的模块缓存机制会让组件使用原始的useNavigate方法,而非你定义的mock函数。

此外,组件逻辑中当user存在且isPending为false时,虽然渲染的是PendingPage,但Login组件本身仍处于挂载状态,useEffect会正常执行,只要mock生效就能捕获到navigate调用。

修复步骤

  1. 调整jest.mock("react-router-dom")的位置,移至测试文件最顶部、所有组件导入语句之前,确保mock在组件加载前生效。
  2. 可简化测试逻辑(无需waitFor,因为useEffect在挂载后同步执行),或保留waitFor兼容潜在的异步场景。

修改后的测试代码

import React from 'react';
import '@testing-library/jest-dom';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';

// 提前mock react-router-dom,确保在Login组件导入前生效
jest.mock("react-router-dom", () => ({
  ...jest.requireActual("react-router-dom"),
  useNavigate: jest.fn()
}));

import { useLogin } from '../hooks/useLogin';
import { useUserContxt } from '../context/authenticationContext';
import Login from './Login';

jest.mock('../hooks/useLogin');
jest.mock('../context/authenticationContext');

describe('Login', () => {
  const mockLogin = jest.fn();
  const mockDispatch = jest.fn();
  // 从mock中获取useNavigate的mock函数
  const mockedUsedNavigate = jest.requireMock("react-router-dom").useNavigate;

  beforeEach(() => {
    useLogin.mockReturnValue({
      login: mockLogin,
      error: null,
    });

    useUserContxt.mockReturnValue({
      user: null,
      isPending: false,
      dispatch: mockDispatch,
    });

    jest.spyOn(console, 'error').mockImplementation(() => {});
    // 重置mock函数
    mockedUsedNavigate.mockClear();
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  afterAll(() => {
    jest.restoreAllMocks();
  });

  // 其他测试用例保持不变...

  it('should navigate to dashboard when user and isPending are false', () => {
    useUserContxt.mockReturnValue({
      user: {},
      isPending: false,
      dispatch: mockDispatch,
    });

    render(
      <BrowserRouter>
        <Login />
      </BrowserRouter>
    );

    // 无需waitFor,直接断言
    expect(mockedUsedNavigate).toHaveBeenCalledWith('/dashboard');
  });
});

额外说明

  • 我们将useNavigate的mock移至文件顶部,并通过jest.requireMock获取mock函数,确保组件使用的是mock版本。
  • 在beforeEach中手动重置mockedUsedNavigate,避免测试用例间的状态污染。
  • 移除了waitFor,因为useEffect在组件挂载后同步执行,可直接断言;若你的user状态更新是异步的,可保留waitFor并配合async/await。

内容的提问来源于stack exchange,提问作者Houssam Mrabet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 15:57:02