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

React测试报错TypeError: source.on is not a function求助

解决测试时的TypeError: source.on is not a function错误

核心问题

你的请求代码存在关键错误:创建FormData后,你没有直接将其作为请求体发送,而是把FormData里的字段提取到普通JavaScript对象data中。当这个对象包含File类型的image字段时,测试环境的MSW(Mock Service Worker)无法正确处理这种非标准请求体格式,从而抛出source.on is not a function错误。生产环境能运行只是依赖了浏览器或后端的兼容处理,但这并非正确实现方式。

修复请求代码

直接传递FormData作为请求体,同时移除手动设置的Content-Type头部(axios会自动为FormData生成带正确boundary的multipart/form-data头部):

images.forEach(async (image) => {
  const config = {
    headers: {
      'Authorization': `Bearer ${user}`
      // 移除手动设置的Content-Type,axios会自动处理
    }
  }

  const formData = new FormData();
  formData.append('workId', JSON.stringify(workId));
  formData.append('image', image.image, image.image.name);

  // 直接传递formData作为data参数,而非普通对象
  await api.post(`${baseUrl}/work/api/images/create/`, formData, config)
  .then((r) => {
    console.log(`created successfully with status code ${r.status}`);
  })
  .catch((e) => {
    console.error(`this is the error: ${e.response.status}`);
  })
})

调整测试代码

确保MSW的mock请求能正确处理标准的FormData格式,还可添加请求体验证逻辑:

it('should returns success on create images', async () => {
  server.use(
    http.post(`${baseUrl}/work/api/images/create/`, async ({ request }) => {
      // 可选:验证FormData内容是否符合预期
      const formData = await request.formData();
      expect(formData.get('workId')).toBe(JSON.stringify(1));
      expect(formData.get('image')).toBeInstanceOf(File);
      
      return new HttpResponse(null, { status: 201 })
    }),
  )

  const user = userEvent.setup();
  
  handerImagesWorkManager(1, 'user');

  const images = [
    new File(['text'], 'image-test.png', { type: 'image/png' }),
    new File(['text'], 'image-test2.png', { type: 'image/png' }),
  ];
  const inputUpload: HTMLInputElement = screen.getByTestId(/upload_images/i);
  await user.upload(inputUpload, images);

  const spy = vi.spyOn(console, 'log');
  const submitButton = await screen.findByDisplayValue(/upload all images/i);

  await user.click(submitButton);

  // 注意:forEach循环会发送多个请求,所以断言调用次数和内容
  expect(spy).toHaveBeenCalledTimes(2);
  expect(spy).toHaveBeenCalledWith("created successfully with status code 201");
});

关键说明

  • 手动设置multipart/form-data头部会丢失boundary信息,这是multipart请求的必要组成部分,axios自动生成的头部才是标准格式。
  • 直接发送FormData是符合multipart请求规范的写法,能同时兼容生产和测试环境。
  • 测试中要注意,forEach循环会触发多次请求,所以console.log会被调用多次,断言时需考虑这一点。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 17:27:55