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

如何在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>
);

额外测试修复

  1. 按钮点击逻辑错误:组件中onClick={handleSubmit(sendFinalPayload(data))}会在渲染时就执行handleSubmit,应改为点击时触发:
onClick={() => handleSubmit(sendFinalPayload(data))}
  1. 测试用例错误:第三个测试中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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 20:42:02