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

Vitest单元测试报错FormData is not a constructor问题

Vitest测试上传工具报FormData is not a constructor修复方案

问题描述

在Vue + Vite项目中使用Vitest编写单元测试,测试封装的Cloudinary图片上传工具函数时,控制台抛出如下错误:

尝试上传图片时发生错误:TypeError: FormData is not a constructor

已确认FormData构造函数首字母大写拼写正确,且已在vite.config.js中将测试运行环境修改为happy-dom,问题仍未解决。

涉及代码

上传工具函数:

import axios from "axios";

const uploadImage = async (file) => {
  if (!file) return;

  try {
    const formData = new FormData();

    const objData = {
      file,
      upload_preset: "journal-vue",
    };

    Object.entries(objData).forEach(([key, value]) => {
      formData.append(key, value);
    });

    const url = "https://api.cloudinary.com/v1_1/christian-door/image/upload";

    const { data } = await axios.post(url, formData);

    return data.secure_url;
  } catch (error) {
    console.log("Ocurrio un error al intentar subir la imagen", error);
    return null;
  }
};

export default uploadImage;

测试用例代码:

import uploadImage from "@/modules/journal/helpers/uploadImage.js";
import axios from "axios";

describe("Test in helper uploadImage", () => {
  test("Must be upload a file and return an url", async () => {
    const url =
      "https://res.cloudinary.com/christian-door/image/upload/v1653891463/fas3px2zm7eq8gt6mfaw.jpg";

    const { data } = await axios.get(url, { responseType: "arraybuffer" });

    const file = new File([data], "image.jpg");

    const urc = await uploadImage(file);

    console.log(urc);
  });
});

排查修复步骤

  • 校验环境配置是否生效
    首先确认happy-dom依赖已经正确安装,执行安装命令:npm i -D happy-dom。
    注意Vitest不会自动读取vite.config.js根级的配置,必须把环境配置写在配置文件的test字段内:
    // vite.config.js
    export default defineConfig({
      // 其他vite配置
      test: {
        environment: 'happy-dom',
        // 其余测试配置
      }
    })
    
    配置修改后必须重启测试进程,清除旧的配置缓存。
  • 手动补全全局Web API挂载
    部分版本的happy-dom和axios组合使用时,不会自动将DOM环境的FormData、File等构造函数注入到全局作用域,导致axios内部调用FormData时找不到构造方法。可以在Vitest的setup文件中手动挂载全局对象:
    1. 新建vitest.setup.js文件,写入以下内容:
    import { beforeAll } from 'vitest'
    beforeAll(() => {
      globalThis.FormData = FormData
      globalThis.File = File
    })
    
    1. 在Vitest配置中引入setup文件:
    test: {
      environment: 'happy-dom',
      setupFiles: ['./vitest.setup.js']
    }
    
  • 优化测试用例,避免请求真实外部接口
    现有测试用例直接调用Cloudinary真实上传接口,既会因为网络问题导致测试不稳定,也容易触发接口限流。应该使用Vitest的mock能力拦截axios请求,只验证工具函数本身的逻辑正确性:
    import { vi, describe, test, expect } from 'vitest';
    import uploadImage from "@/modules/journal/helpers/uploadImage.js";
    import axios from "axios";
    
    // mock axios模块
    vi.mock('axios');
    
    describe("Test in helper uploadImage", () => {
      test("Must call upload api and return secure url when file passed", async () => {
        // 模拟接口返回
        axios.post.mockResolvedValue({
          data: {
            secure_url: 'https://test-upload-url.com/demo.jpg'
          }
        })
        // 构造测试文件
        const testFile = new File(['test binary content'], "image.jpg");
        const result = await uploadImage(testFile);
        // 校验返回值
        expect(result).toBe('https://test-upload-url.com/demo.jpg');
        // 校验请求参数格式正确
        expect(axios.post).toHaveBeenCalledTimes(1);
        const [, requestPayload] = axios.post.mock.calls[0];
        expect(requestPayload instanceof FormData).toBe(true);
      });
    
      test("Must return undefined when no file passed", async () => {
        const result = await uploadImage(null);
        expect(result).toBeUndefined();
        expect(axios.post).not.toHaveBeenCalled();
      })
    });
    
  • 切换测试环境为jsdom
    如果上述操作后问题仍存在,可以更换Web API兼容度更成熟的jsdom作为测试环境:
    1. 安装依赖:npm i -D jsdom
    2. 修改Vitest配置中environment字段值为jsdom,重启测试进程即可。

内容的提问来源于stack exchange,提问作者Christian Osorio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 08:30:48