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

Jest:如何模拟组件子元素的函数?附React组件示例

如何用Jest模拟React组件子元素的相关函数?

先来看看你提供的Columns组件代码:

class Columns extends React.Component {
  fetchCSVData = async () => {
    const { ownerId } = this.props;
    const CSVTrackResponse = await getCSVClientReport({ ownerId });
    fileDownload(CSVTrackResponse, 'lifetimeTracks.csv');
  };
  render() {
    const children = React.Children.map(this.props.children, child => {
      return React.cloneElement(child, { onDownload: () => this.fetchCSVData() });
    });
    return <ColumnsContainer>{children}</ColumnsContainer>;
  }
}

这个组件的核心逻辑是给所有子元素传递onDownload属性,当子元素触发这个方法时,会执行异步的CSV下载流程。咱们可以分步骤用Jest来测试这个逻辑,同时模拟子元素的交互。


步骤1:模拟外部依赖

首先得把组件里用到的外部函数getCSVClientReport和fileDownload给mock掉,这样测试时不会真的发起网络请求或者触发浏览器下载行为:

// 替换成你实际的文件路径
jest.mock('../utils/getCSVClientReport', () => jest.fn(() => Promise.resolve('mock-csv-content')));
// 假设fileDownload来自file-saver库,根据你的实际依赖调整
jest.mock('file-saver', () => ({ fileDownload: jest.fn() }));

步骤2:创建模拟子组件

为了测试子元素是否正确接收并触发onDownload,咱们可以写一个极简的模拟子组件,比如一个点击就调用onDownload的按钮:

const MockChild = ({ onDownload, 'data-testid': testId }) => (
  <button onClick={onDownload} data-testid={testId}>
    下载CSV
  </button>
);

步骤3:编写测试用例

这里咱们用@testing-library/react来编写测试(你也可以用Enzyme,不过Testing Library更贴近用户真实行为):

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import Columns from './Columns';
import getCSVClientReport from '../utils/getCSVClientReport';
import { fileDownload } from 'file-saver';

describe('Columns组件', () => {
  // 每次测试前清空mock的调用记录,避免测试相互干扰
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('应该给子元素传递onDownload属性,点击时触发下载流程', async () => {
    // 渲染Columns组件,并传入模拟子元素
    render(
      <Columns ownerId="user-123">
        <MockChild />
      </Columns>
    );

    // 找到下载按钮并模拟点击
    const downloadBtn = screen.getByText('下载CSV');
    fireEvent.click(downloadBtn);

    // 验证getCSVClientReport是否用正确的参数调用了
    expect(getCSVClientReport).toHaveBeenCalledWith({ ownerId: 'user-123' });

    // 等待异步操作完成,验证fileDownload是否被正确调用
    await waitFor(() => {
      expect(fileDownload).toHaveBeenCalledWith('mock-csv-content', 'lifetimeTracks.csv');
    });
  });

  it('应该给所有子元素都传递onDownload属性', () => {
    // 渲染带有多个子元素的Columns组件
    render(
      <Columns ownerId="user-456">
        <MockChild data-testid="child-btn-1" />
        <MockChild data-testid="child-btn-2" />
      </Columns>
    );

    // 点击第一个子按钮,验证调用
    const btn1 = screen.getByTestId('child-btn-1');
    fireEvent.click(btn1);
    expect(getCSVClientReport).toHaveBeenCalledWith({ ownerId: 'user-456' });

    // 清空mock记录,测试第二个子按钮
    jest.clearAllMocks();
    const btn2 = screen.getByTestId('child-btn-2');
    fireEvent.click(btn2);
    expect(getCSVClientReport).toHaveBeenCalledWith({ ownerId: 'user-456' });
  });
});

关键要点说明

  • jest.clearAllMocks():每次测试前清空mock的调用记录,确保不同测试用例之间不会互相干扰。
  • waitFor:因为fetchCSVData是异步函数,必须用waitFor等待异步操作完成后再验证结果。
  • 模拟子组件:通过自定义模拟子组件,咱们可以精准控制交互行为,验证父组件是否正确传递了回调属性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:06:01