Mock useNavigate未触发调用,Jest测试失败问题排查
场景还原
我在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
相关产品推荐
相关产品推荐

