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

使用Jest模拟NextJS router事件时报Cannot read property 'on' of undefined如何解决

问题原因及解决方案

核心问题

  1. mock 时机错误:你在 it 测试块中才定义 useRouter 的 mock 实现,此时组件已经在 beforeEach 阶段完成挂载,组件内部 useEffect 执行时调用的 useRouter 还没有返回正确的结构,events 属性为 undefined,自然访问 on 方法时报错。
  2. mock 实例未绑定:你在测试块中单独声明的 let useRouter = jest.fn() 是孤立的 mock 函数,和 next/router 模块导出的 useRouter 没有任何关联,mock 规则完全没有生效。

修改方案

首先在测试文件顶部绑定 next/router 的 mock 规则,再在组件挂载前提前配置 useRouter 的返回值,确保组件渲染时就能拿到符合预期的 router 结构。

修改后的测试代码示例:

import { mount, ReactWrapper } from 'enzyme';
import FavoritesPage from 'pages/user/favorites';
import configureStore, { MockStore } from 'redux-mock-store';
import storeInitialState from '__tests__/unit/support/storeInitialState';
import { Provider } from 'react-redux';
import { waitFor } from '@testing-library/react';
import { RequestStates } from 'types/State';
import useAdSetup from 'lib/hooks/useAdSetup';
// 新增导入next/router的useRouter
import { useRouter } from 'next/router';

const mockRequestState = RequestStates.Finished;

jest.mock('lib/Auth');
jest.mock('lib/EventLogger');
jest.mock('lib/hooks/useAdSetup');

jest.mock('lib/hooks/useFetchFavorites', () => {
  return (): { requestState: RequestStates } => {
    return {
      requestState: mockRequestState,
    };
  };
});

// 修改next/router的mock规则,绑定useRouter为mock函数
jest.mock('next/router', () => ({
  useRouter: jest.fn()
}));

const mockStore = configureStore();

let store: MockStore;

describe('when clicking the price drop subnav button', () => {
  let component: ReactWrapper;
  let eventName;
  let routeChangeHandler;

  beforeEach(async () => {
    store = mockStore(storeInitialState);
    // 组件挂载前提前配置useRouter的返回值
    useRouter.mockImplementation(() => {
      return {
        events: {
          on: jest.fn((event, callback) => {
            eventName = event;
            routeChangeHandler = callback;
          }),
          off: jest.fn((event, callback) => {
            eventName = event;
            routeChangeHandler = callback;
          }),
          // 如果组件用到其他router属性(如pathname、query)可以在此补充
        },
      };
    });
    await waitFor(() => {
      component = mount(
        <Provider store={store}>
          <FavoritesPage />
        </Provider>
      );
    });
    component.find('.price-drop-nav-item').simulate('click');
  });

  it('shows price drops', () => {
    expect(useRouter).toHaveBeenCalledTimes(1);
    expect(component.find('.price-drop-nav-item').hasClass('active')).toBeTruthy();
  });
});

如果你的组件是使用withRouter高阶组件注入的router而非useRouter Hook,需要调整next/router的mock规则为:

jest.mock('next/router', () => ({
  useRouter: jest.fn(),
  withRouter: (Component) => (props) => <Component 
    {...props} 
    router={{
      events: {
        on: jest.fn((event, callback) => {
        eventName = event;
        routeChangeHandler = callback;
      },
      off: jest.fn((event, callback) => {
        eventName = event;
        routeChangeHandler = callback;
      }),
    }
  }} />
}));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 03:24:03