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
相关产品推荐
相关产品推荐

