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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 09:55:31