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

Mock useNavigate未触发调用,Jest测试失败问题排查

问题:Mock useNavigate后点击跳转测试失败

场景还原

我在Login.js中使用react-router-dom的useNavigate实现跳转:

// Login.js
import { useNavigate } from 'react-router-dom';

function Login() {
  const navigate = useNavigate();
  return (
    <div onClick={() => navigate("/register")}>Create</div>
  );
}

编写测试时,不MockuseNavigate能正常运行,但Mock后测试失败,测试代码如下:

test('test navigate to register', () => { 
  const mockedUsedNavigate = jest.fn();
  jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
    useNavigate: () => mockedUsedNavigate
  }));

  render(
    <Router>
      <Login />
    </Router>,
  );

  const registerButton = screen.getByText(/Create/i);
  fireEvent.click(registerButton);
  expect(mockedUsedNavigate).toHaveBeenCalled();
}); 

报错信息:

expect(jest.fn()).toHaveBeenCalled()

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

我的目标是拆分单元测试:一个验证点击功能正常(已通过),另一个验证点击时navigate被正确调用(避免真实跳转)。


问题原因

Jest的jest.mock会被自动提升到整个模块的最顶部执行,而你在test函数内部定义的mockedUsedNavigate是在mock执行之后才初始化的。这就导致mock的useNavigate返回的是一个指向未定义变量的函数,最终navigate实际是undefined,点击自然不会触发调用。


解决方案

方案1:将Mock移到模块顶部,使用mockImplementation

把jest.mock放在测试文件的最外层,通过require获取mock的useNavigate并修改其实现:

// 模块顶部定义mock
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useNavigate: jest.fn()
}));

test('test navigate to register', () => { 
  const mockedUsedNavigate = require('react-router-dom').useNavigate;
  // 重置mock状态,避免跨测试污染
  mockedUsedNavigate.mockReset();

  render(
    <Router>
      <Login />
    </Router>,
  );

  const registerButton = screen.getByText(/Create/i);
  fireEvent.click(registerButton);
  
  expect(mockedUsedNavigate).toHaveBeenCalledWith("/register");
}); 

方案2:使用jest.spyOn动态Mock(更适合单测试用例)

这种方式不需要提前mock整个模块,而是在单个测试中替换useNavigate的实现:

import { useNavigate } from 'react-router-dom';

test('test navigate to register', () => { 
  const mockedUsedNavigate = jest.fn();
  // 用spyOn替换useNavigate的实现
  jest.spyOn(require('react-router-dom'), 'useNavigate').mockReturnValue(mockedUsedNavigate);

  render(
    <Router>
      <Login />
    </Router>,
  );

  const registerButton = screen.getByText(/Create/i);
  fireEvent.click(registerButton);
  
  expect(mockedUsedNavigate).toHaveBeenCalledWith("/register");
  
  // 测试结束后恢复原实现
  jest.restoreAllMocks();
}); 

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 10:27:27