如何在Jest测试中Mock Context Provider与withUserDetails函数
问题
刚接触Jest测试,当前测试配置下组件中的Context返回undefined,不确定如何在Jest中Mock withUserDetails函数,怀疑这是问题根源。此前测试表单Provider时未通过函数包装,可直接传入Context并成功完成测试,现寻求解决办法。
模态框组件
<UserContext.Consumer> {(data) => ( <Box sx={{ alignItems: 'center', borderTop: '1px solid rgb(241,241,242)', display: 'flex', justifyContent: 'flex-end', marginTop: '1.125rem', minHeight: '4rem', padding: '1.5rem 1rem .75rem', }} > {console.log('user', data)} <Button aria-label="Cancel confirm request" onClick={handleClose} sx={{ borderRadius: '0', padding: '0 .5rem' }} data-testid={'cancelBtn'} > Cancel </Button> <Button className="search" variant="contained" aria-label="Send Request" onClick={handleSubmit(sendFinalPayload(data))} data-testid={'sendRequestBtn'} > Send Request </Button> </Box> )} </UserContext.Consumer> export default withUserDetails(ConfirmModal);
Context代码
export const withUserDetails = (Component) => { const UserComponent = (props) => { const { data } = fetchUser(); return ( <UserContext.Provider value={data}> <Component {...props} /> </UserContext.Provider> ); }; return UserComponent; };
测试代码
import React from 'react'; import { screen, render, fireEvent } from 'test-utils'; import Modal from '~/components/modal/confirm.js'; import { UserContext } from '~/shared/contexts/index'; const setupModalConfirm = (props = { context: {} }) => { const { context, ...overrides } = props; const handleClose = jest.fn(); const defaultPropsConfirm = { open: true, handleClose }; const userContext = { user: { displayName: "User", userId: "UserId", permissions: { isAuthenticated: true, } } } const utils = render( <UserContext.Provider value={{ ...userContext, ...context }}> <Modal {...defaultPropsConfirm} {...overrides} /> </UserContext.Provider> ); const instance = utils.baseElement; return { ...utils, instance, handleClose }; }; it('renders correctly and opens modal', () => { const { instance } = setupModalConfirm(); expect(instance).toMatchSnapshot(); }); it('closes the modal', () => { const { handleClose } = setupModalConfirm(); const button = screen.getByTestId('cancelBtn'); fireEvent.click(button); expect(handleClose).toBeCalledTimes(1); }); it('can call confirm', async () => { setupModalConfirm(); const button = screen.getByTestId('sendRequestBtn'); fireEvent.click(button); expect(button).toBeCalledTimes(1); });
解决方案
核心问题分析
你的Modal组件默认导出的是被withUserDetails包装后的版本,测试时Jest会执行该高阶组件内的fetchUser(),但测试环境中这个函数大概率返回undefined,导致Context值为空。以下是三种可行解决方法:
方法1:Mock withUserDetails 高阶组件
直接让Mock后的高阶组件返回原组件,跳过它的包装逻辑,这样你手动传入的UserContext.Provider就能生效:
// 在测试文件顶部添加Mock(根据实际路径调整) jest.mock('~/shared/contexts/index', () => ({ ...jest.requireActual('~/shared/contexts/index'), withUserDetails: (Component) => Component, // 直接返回原始组件 }));
方法2:Mock fetchUser 函数
如果需要保留withUserDetails的包装逻辑,只需MockfetchUser让它返回预设的Context数据:
// 在测试文件顶部添加Mock(根据实际路径调整) jest.mock('~/shared/contexts/index', () => ({ ...jest.requireActual('~/shared/contexts/index'), fetchUser: () => ({ data: { user: { displayName: "User", userId: "UserId", permissions: { isAuthenticated: true, } } } }), }));
方法3:导出未包装的原始组件
在Modal组件文件中额外导出未被withUserDetails包装的原始组件:
// ConfirmModal组件文件末尾 export const UnwrappedConfirmModal = ConfirmModal; export default withUserDetails(ConfirmModal);
测试时导入并使用这个未包装的组件,和你之前测试表单Provider的方式一致:
// 测试文件中修改导入 import Modal, { UnwrappedConfirmModal } from '~/components/modal/confirm.js'; // setup函数中替换为未包装组件 const utils = render( <UserContext.Provider value={{ ...userContext, ...context }}> <UnwrappedConfirmModal {...defaultPropsConfirm} {...overrides} /> </UserContext.Provider> );
额外测试修复
- 按钮点击逻辑错误:组件中
onClick={handleSubmit(sendFinalPayload(data))}会在渲染时就执行handleSubmit,应改为点击时触发:
onClick={() => handleSubmit(sendFinalPayload(data))}
- 测试用例错误:第三个测试中
expect(button).toBeCalledTimes(1)无效,DOM元素不能用toBeCalledTimes,应MockhandleSubmit并检查它的调用次数:
// 修改setup函数 const setupModalConfirm = (props = { context: {} }) => { const { context, ...overrides } = props; const handleClose = jest.fn(); const handleSubmit = jest.fn(); // 添加Mock const defaultPropsConfirm = { open: true, handleClose, handleSubmit // 传入组件 }; // ... 其他代码 return { ...utils, instance, handleClose, handleSubmit // 返回Mock函数 }; }; // 修改测试用例 it('can call confirm', async () => { const { handleSubmit } = setupModalConfirm(); const button = screen.getByTestId('sendRequestBtn'); fireEvent.click(button); expect(handleSubmit).toBeCalledTimes(1); });
内容的提问来源于stack exchange,提问作者user2677350
相关产品推荐
相关产品推荐

