javascript测试中模拟文件上传进度的核心是拦截网络请求并手动触发progress事件,常用jest mock xmlhttprequest、msw构造流响应或重构为可注入onprogress参数,同时注意filereader场景的mock。

在 JavaScript 测试中模拟文件上传进度通知,核心是拦截实际的网络请求(如 fetch 或 XMLHttpRequest),并手动触发进度事件。常用方式是结合测试框架(如 Jest)与 Mock 工具(如 jest.mock、msw 或自定义 FileReader/XMLHttpRequest 行为)来控制进度回调的调用时机和参数。
用 Jest 模拟 XMLHttpRequest 并手动派发 progress 事件
如果代码中直接使用 XMLHttpRequest 上传文件,并监听 upload.onprogress,可在测试中替换其构造函数,返回一个可控制的 mock 实例:
- 重写
XMLHttpRequest.prototype.upload.addEventListener,捕获"progress"监听器 - 在 mock 的
send()中,定时多次调用捕获到的回调函数,传入带loaded和total的事件对象 - 确保
event.lengthComputable = true,否则多数进度逻辑会跳过
示例片段:
beforeEach(() => {
const originalXHR = window.XMLHttpRequest;
global.XMLHttpRequest = jest.fn().mockImplementation(() => {
const xhr = new originalXHR();
const upload = { addEventListener: jest.fn() };
Object.defineProperty(xhr, 'upload', { value: upload });
// 捕获 onprogress 回调
let progressCallback;
upload.addEventListener.mockImplementation((type, cb) => {
if (type === 'progress') progressCallback = cb;
});
xhr.send = jest.fn(() => {
// 模拟分段触发进度(0% → 50% → 100%)
setTimeout(() => progressCallback({
lengthComputable: true,
loaded: 0,
total: 100000
}), 10);
setTimeout(() => progressCallback({
lengthComputable: true,
loaded: 50000,
total: 100000
}), 50);
setTimeout(() => {
progressCallback({
lengthComputable: true,
loaded: 100000,
total: 100000
});
xhr.readyState = 4;
xhr.status = 200;
xhr.onload && xhr.onload();
}, 100);
});
return xhr;
});
});
用 MSW(Mock Service Worker)配合自定义响应流模拟上传进度
若上传走的是 fetch,MSW 更适合——它本身不直接支持上传进度,但可通过 Response.stream() + ReadableStream 构造一个“缓慢吐出”的响应体,再在被测代码中用 upload.onprogress(需封装 fetch 为类 XHR 行为)或通过 AbortController + 定时器模拟进度更新。
- 更推荐做法:在被测代码中将上传逻辑抽象为可注入的“进度发射器”,测试时传入 mock 函数代替真实 XHR/fetch
- MSW 可用于断言请求是否发出、参数是否正确,而进度逻辑由单元测试直接驱动
重构上传逻辑,让进度通知可测试(推荐)
比起深度 mock 原生 API,更健壮的方式是把进度通知解耦为可注入的依赖:
- 将上传函数设计为接收一个
onProgress: (progress: { loaded: number; total: number }) => void参数 - 真实环境里传入基于
XMLHttpRequest.upload.onprogress的回调;测试时传入 jest.fn() 收集调用记录 - 手动调用该函数多次,模拟不同阶段,然后 expect 其被调用且参数符合预期
这样无需 mock 浏览器底层,测试更轻量、稳定、易读。
注意 FileReader 与 Blob 的配合场景
某些上传前预览或分片逻辑会用 FileReader 读取文件,再通过 onprogress 获取读取进度。此时可 mock FileReader.prototype.readAsArrayBuffer 等方法,用 setTimeout 分阶段调用 onprogress 和 onload。
- 关键点:设置
reader.length和reader.loaded(需通过 defineProperty 模拟不可写属性) - 或更简单:直接调用
reader.onload和reader.onprogress,传入伪造事件
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











