如何用Mock Service Worker模拟axios的onUploadProgress配置?
解决MSW拦截带onUploadProgress的axios请求问题并测试进度事件
1. 修正MSW请求匹配规则
当axios配置了onUploadProgress时,会自动为multipart/form-data请求添加boundary参数(如multipart/form-data; boundary=----WebKitFormBoundaryxxxx),如果MSW的handler严格匹配Content-Type: multipart/form-data,会导致匹配失败,请求无法被拦截。
调整handler的匹配规则,使用正则匹配以multipart/form-data开头的请求头:
import { http, HttpResponse } from 'msw'; export const handlers = [ http.put( '/uploadFile/files', async ({ request, ctx }) => { // 返回模拟响应 return ctx.json({ message: 'File uploaded successfully' }); }, { // 匹配所有multipart/form-data类型的请求,忽略boundary headers: { 'Content-Type': /^multipart\/form-data/, }, } ), ];
2. 模拟上传进度事件
上传进度(onUploadProgress)是客户端向服务器发送数据时触发的事件,需要在MSW中手动模拟XHR的upload.progress事件:
在测试环境的MSW配置文件(如setupTests.ts)中,添加请求监听逻辑,触发进度事件:
import { setupServer } from 'msw/node'; import { handlers } from './mocks/handlers'; const server = setupServer(...handlers); beforeAll(() => { server.listen(); // 监听目标请求,模拟上传进度 server.events.on('request:start', ({ request }) => { if (request.method === 'PUT' && request.url.pathname === '/uploadFile/files') { // 获取原始XHR实例(axios带onUploadProgress会用XHR发送请求) const xhr = request.raw as XMLHttpRequest; if (!xhr) return; const totalFileSize = 1000000; // 模拟文件总大小 let loadedSize = 0; const chunkSize = 200000; // 每次进度更新的增量 const interval = setInterval(() => { loadedSize += chunkSize; // 创建并触发progress事件 const progressEvent = new ProgressEvent('progress', { loaded: loadedSize, total: totalFileSize, lengthComputable: true, }); xhr.upload.dispatchEvent(progressEvent); // 进度达到100%后停止 if (loadedSize >= totalFileSize) { clearInterval(interval); } }, 100); // 每100ms更新一次进度 } }); }); afterAll(() => server.close()); afterEach(() => server.resetHandlers());
3. 编写测试用例验证进度
使用React Testing Library和Jest编写测试,验证进度更新和最终结果:
import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import FileUploader from './FileUploader'; test('uploads file and updates progress correctly', async () => { const user = userEvent.setup(); // 渲染上传组件 render(<FileUploader />); // 创建模拟文件 const testFile = new File(['dummy content'], 'test.pdf', { type: 'application/pdf' }); // 获取文件输入框并上传文件 const fileInput = screen.getByRole('textbox', { name: /select file/i }); await user.upload(fileInput, testFile); // 验证进度逐步更新(这里以最终显示100%为例) await waitFor(() => { expect(screen.getByText('100%')).toBeInTheDocument(); }); // 验证上传成功提示 await waitFor(() => { expect(screen.getByText('File uploaded successfully')).toBeInTheDocument(); }); });
额外说明
- 如果你的
apiClient.doPut是封装后的axios方法,确保它没有覆盖onUploadProgress配置,也没有修改请求头导致MSW匹配失败。 - 若不需要精确模拟进度增量,也可以直接在handler中一次性触发100%进度的事件,但分块触发更贴近真实场景。
内容的提问来源于stack exchange,提问作者Chetan Kondawle
相关产品推荐
相关产品推荐

