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

如何在Jest同测试用例中两次Mock React的同一模块?

在React组件渲染后修改Mock返回值的解决方案

你的核心问题是组件仅在挂载时调用一次useUser,直接链式调用mockReturnValueOnce无法触发状态更新,因为组件不会重复调用该Hook。以下是两种可行的解决方案:

方法一:通过可变变量控制Mock返回值 + 重新渲染组件

  1. 在Mock模块中定义一个可变变量存储用户状态,同时暴露修改该状态的方法
  2. 渲染组件后,修改状态变量并调用rerender触发组件重渲染,让useUser返回新值

代码示例

首先配置Mock:

jest.mock('assets/state/hooks/firebaseHooks', () => {
  // 初始化状态为已认证
  let userState = [true, false];
  return {
    ...jest.requireActual('assets/state/hooks/firebaseHooks'),
    useUser: jest.fn(() => userState),
    // 暴露修改状态的方法供测试用例调用
    __setUserState: (newState) => { userState = newState; }
  };
});

然后编写测试用例:

it('should redirect to login when user authentication status changes to unauthenticated', () => {
  // 获取rerender方法
  const { rerender } = render(
    <RecoilRoot>
      <BrowserRouter>
        <AddCategory />
      </BrowserRouter>
    </RecoilRoot>
  );

  // 初始状态:已认证,验证未跳转
  expect(mockedNavegacao).not.toBeCalled();

  // 修改Mock的用户状态为未认证
  const { __setUserState } = require('assets/state/hooks/firebaseHooks');
  __setUserState([false, false]);

  // 重新渲染组件,触发useEffect监听user变化
  rerender(
    <RecoilRoot>
      <BrowserRouter>
        <AddCategory />
      </BrowserRouter>
    </RecoilRoot>
  );

  // 验证是否触发登录页跳转
  expect(mockedNavegacao).toHaveBeenCalledWith('/');
});

方法二:使用引用类型存储状态

利用对象的引用特性,直接修改对象内部属性即可更新Mock返回值,无需重新赋值Mock函数:

代码示例

// 用对象存储状态(引用类型,修改内部值无需重新绑定Mock)
const mockUserState = { value: [true, false] };

jest.mock('assets/state/hooks/firebaseHooks', () => ({
  ...jest.requireActual('assets/state/hooks/firebaseHooks'),
  useUser: jest.fn(() => mockUserState.value)
}));

it('should redirect when user logs out', () => {
  const { rerender } = render(
    <RecoilRoot>
      <BrowserRouter>
        <AddCategory />
      </BrowserRouter>
    </RecoilRoot>
  );

  expect(mockedNavegacao).not.toBeCalled();

  // 直接修改状态值
  mockUserState.value = [false, false];
  // 重新渲染组件
  rerender(
    <RecoilRoot>
      <BrowserRouter>
        <AddCategory />
      </BrowserRouter>
    </RecoilRoot>
  );

  expect(mockedNavegacao).toHaveBeenCalledWith('/');
});

关键说明

组件中的useEffect依赖user变化,所以必须通过rerender让组件重新执行useUser获取新的Mock值,才能触发useEffect的逻辑判断。直接修改Mock返回值但不重新渲染的话,组件不会感知到状态变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 19:35:27