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

React-Dropzone-Uploader 文件上传Jest测试问题求助

解决Jest测试中react-dropzone-uploader的XMLHttpRequest回调触发问题

问题根源

Jest的jsdom环境对XMLHttpRequest的模拟逻辑极简,调用send()后不会自动触发onReadyStateChange回调,而react-dropzone-uploader依赖这个回调来更新上传状态至done,这就是测试中状态无法推进的核心原因。

解决方案

1. 手动触发XMLHttpRequest的onReadyStateChange

触发drop事件后,手动拦截并获取组件内部创建的XHR实例,模拟请求完成的状态并调用回调:

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import YourUploadComponent from './YourUploadComponent';

test('上传流程能正常完成', async () => {
  render(<YourUploadComponent />);
  
  // 创建测试文件
  const testFile = new File(['测试内容'], 'test.txt', { type: 'text/plain' });
  
  // 触发drop事件(对应你已实现的步骤)
  const dropZone = screen.getByTestId('drop-zone'); // 假设组件设置了data-testid
  fireEvent.drop(dropZone, {
    dataTransfer: {
      files: [testFile],
    },
  });
  
  // 拦截XHR创建,保存实例
  const originalXHR = window.XMLHttpRequest;
  let targetXHR: XMLHttpRequest | null = null;
  window.XMLHttpRequest = jest.fn(() => {
    targetXHR = new originalXHR();
    return targetXHR;
  }) as any;
  
  // 等待组件发起XHR请求
  await new Promise(resolve => setTimeout(resolve, 0));
  
  if (targetXHR) {
    // 模拟请求成功完成的状态
    targetXHR.readyState = 4;
    targetXHR.status = 200;
    targetXHR.responseText = JSON.stringify({ code: 200, msg: '上传成功' });
    
    // 手动调用onreadystatechange回调
    targetXHR.onreadystatechange?.call(targetXHR);
  }
  
  // 验证上传状态是否更新为done
  await waitFor(() => {
    expect(screen.getByText('上传完成')).toBeInTheDocument();
  });
  
  // 恢复原始XHR构造函数
  window.XMLHttpRequest = originalXHR;
});

2. 全局Mock XMLHttpRequest,自动触发回调

如果多个测试用例都需要处理上传逻辑,可以在测试全局配置中Mock XMLHttpRequest,让它在send()后自动模拟请求完成:

在setupTests.ts中添加:

class MockXHR extends XMLHttpRequest {
  override send(body?: Document | XMLHttpRequestBodyInit | null): void {
    super.send(body);
    // 异步模拟请求完成
    setTimeout(() => {
      this.readyState = 4;
      this.status = 200;
      this.responseText = JSON.stringify({ success: true });
      // 触发组件依赖的回调
      this.onreadystatechange?.call(this);
      this.onload?.call(this);
    }, 0);
  }
}

window.XMLHttpRequest = MockXHR as any;

之后测试用例可简化为:

test('通过全局Mock完成上传测试', async () => {
  render(<YourUploadComponent />);
  
  const testFile = new File(['测试内容'], 'test.txt', { type: 'text/plain' });
  const dropZone = screen.getByTestId('drop-zone');
  
  fireEvent.drop(dropZone, {
    dataTransfer: { files: [testFile] },
  });
  
  // 等待异步状态更新
  await waitFor(() => {
    expect(screen.getByText('上传完成')).toBeInTheDocument();
  });
});

3. 关键注意事项

  • 若组件需手动触发上传(比如点击上传按钮),测试中要额外触发对应操作。
  • 如果上传接口需要特定响应结构,可在MockXHR中根据请求URL或请求体自定义返回内容。
  • 必须使用waitFor或findBy*系列查询等待异步状态更新,避免测试竞态条件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 19:35:24