如何在Jest同测试用例中两次Mock React的同一模块?
在React组件渲染后修改Mock返回值的解决方案
你的核心问题是组件仅在挂载时调用一次useUser,直接链式调用mockReturnValueOnce无法触发状态更新,因为组件不会重复调用该Hook。以下是两种可行的解决方案:
方法一:通过可变变量控制Mock返回值 + 重新渲染组件
- 在Mock模块中定义一个可变变量存储用户状态,同时暴露修改该状态的方法
- 渲染组件后,修改状态变量并调用
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
相关产品推荐
相关产品推荐

