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文件中手动挂载全局对象:- 新建
vitest.setup.js文件,写入以下内容:
import { beforeAll } from 'vitest' beforeAll(() => { globalThis.FormData = FormData globalThis.File = File })- 在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作为测试环境:- 安装依赖:
npm i -D jsdom - 修改Vitest配置中environment字段值为
jsdom,重启测试进程即可。
- 安装依赖:
内容的提问来源于stack exchange,提问作者Christian Osorio
相关产品推荐
相关产品推荐

