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

Mock的useNavigation触发但Jest的toHaveBeenCalledWith未识别问题

React Navigation useNavigation Mock后Jest无法捕获调用的问题解决

问题描述

我Mock了@react-navigation/native中的useNavigation()钩子,但Jest始终报错称该函数未被调用,可日志显示函数明明已经触发。尝试用waitFor包裹断言时,还出现TypeScript错误:Error: Uncaught [TypeError: Cannot read properties of undefined (reading 'catch')]。

测试代码

jest.mock('@react-navigation/native', () => {
  const actualNav = jest.requireActual('@react-navigation/native');
  return {
    ...actualNav,
    useNavigation: () => ({
      // 换成() => { console.log('trigger') }时能看到触发日志
      navigate: jest.fn(),
    }),
    useRoute: () => ({
      params: { email: '' },
    }),
  };
});
....

it.only('should navigate to resend screen when button is pressed', async () => {
  const { getByTestId } = render(
    withStoreProvider(<EmailVerificationPending />),
  );

  await waitFor(() => {
    expect(
      getByTestId('emailVerificationPendingScreen_moreInfoCta'),
    ).toBeTruthy();
  });

  fireEvent.press(getByTestId('emailVerificationPendingScreen_moreInfoCta'));

  expect(navigation.navigate).toHaveBeenCalledWith(
    RootScreens.SignUpNavigator,
    {
      screen: SignUpScreens.EmailVerificationResend,
      params: {
        email: '',
      },
    },
  );
});

报错信息

● features/signup/presentation/email-verification/pending-screen › should navigate to resend screen when button is pressed

expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: "RootScreens_SignUpNavigator", {"params": {"email": ""}, "screen": "SignUpScreens_EmailVerificationResend"}

Number of calls: 0

  64 |     fireEvent.press(getByTestId('emailVerificationPendingScreen_moreInfoCta'));
  65 |
> 66 |     expect(navigation.navigate).toHaveBeenCalledWith(

解决方法

1. 确保Mock的navigate函数引用一致

当前Mock的useNavigation每次调用都会返回新的jest.fn()实例,导致测试代码中引用的navigation.navigate和组件实际调用的不是同一个函数。需要提前定义mock函数,让每次调用useNavigation都返回同一个对象:

// 提前定义mock函数,确保全局引用一致
const mockNavigate = jest.fn();

jest.mock('@react-navigation/native', () => {
  const actualNav = jest.requireActual('@react-navigation/native');
  return {
    ...actualNav,
    useNavigation: () => ({
      navigate: mockNavigate,
    }),
    useRoute: () => ({
      params: { email: '' },
    }),
  };
});

然后在测试中直接使用这个提前定义的mock函数进行断言:

it.only('should navigate to resend screen when button is pressed', async () => {
  const { getByTestId } = render(
    withStoreProvider(<EmailVerificationPending />),
  );

  // 等待元素渲染完成,建议用toBeInTheDocument替代toBeTruthy
  await waitFor(() => {
    expect(getByTestId('emailVerificationPendingScreen_moreInfoCta')).toBeInTheDocument();
  });

  fireEvent.press(getByTestId('emailVerificationPendingScreen_moreInfoCta'));

  // 直接使用全局的mockNavigate断言
  expect(mockNavigate).toHaveBeenCalledWith(
    RootScreens.SignUpNavigator,
    {
      screen: SignUpScreens.EmailVerificationResend,
      params: {
        email: '',
      },
    },
  );
});

2. 重置Mock避免测试污染

在每个测试前重置mock函数,防止其他测试的调用记录影响当前测试:

beforeEach(() => {
  mockNavigate.mockClear();
});

3. 解决waitFor的catch错误

该错误通常是因为组件内部存在未被正确Mock的异步操作(比如API请求、Redux异步Action),导致出现未处理的Promise rejection。需要Mock这些异步逻辑,让它们立即resolve:

  • 如果组件中有调用Redux异步Action,需要Mock对应的thunk/saga,确保它们不会发起真实请求,而是直接返回成功结果。
  • 如果有API调用,需要Mock对应的请求函数,让它返回resolved的Promise。

4. 替换断言方法

建议使用toBeInTheDocument替代toBeTruthy,这是@testing-library/jest-dom提供的更精准的DOM元素存在断言,需要确保已导入该库的匹配器(通常在setupTests.js中配置)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 06:56:54