React中用Jest测试fetch调用时FormData为何显示为空对象?
问题:Jest测试包含FormData的fetch调用时,body显示为空对象
业务代码
export const submitform = async (url:string, file:File, params:{}) => { const body = new FormData() body.append('file', file) if (Object.keys(params).length > 0) { Object.keys(params).forEach(key => { body.append(key, params[key]) }) } const res = await fetch(url, { method: 'post', headers: { authorization: `Bearer ${'abc'}` }, body }) return res.json() }
测试代码
const myParams = { a: 'a', b: 'b' } const response = await submitform(postUrl, file, myParams) expect(fetch).toHaveBeenCalledTimes(1) expect(fetch).toHaveBeenCalledWith( "/url", expect.objectContaining({ method: 'post', headers: { authorization: `Bearer ${'abc'}` }, body: myParams }) )
报错信息
Error: expect(jest.fn()).toHaveBeenCalledWith(...expected) Expected: "/url", ObjectContaining {"body": {"a": "a", "b": "b"}, "headers": {"authorization": "Bearer abc"}, "method": "post"} Received: "/url", {"body": {}, "headers": {"authorization": "Bearer abc"}, "method": "post"} Number of calls: 1
日志打印结果
BODY FormData {} FILE File {} A a B b
疑问
明明日志能获取到FormData中的数据,为什么fetch调用中的body会显示为空对象?
原因与解决方案
核心原因
- FormData的特殊性:FormData是浏览器原生的特殊对象,Jest在打印或断言时无法直接将其序列化为普通对象,所以会显示为
{},但内部数据实际存在。 - 断言逻辑错误:测试代码里直接断言
body等于普通对象myParams,但业务代码传入的是FormData实例,两者类型不匹配,必然断言失败。
解决方案
要正确测试FormData,需手动验证其内部内容,而非直接断言等于普通对象,以下是两种可行方式:
方式1:封装辅助函数验证FormData
// 辅助函数:校验FormData的内容 const validateFormData = (formData: FormData, expectedParams: Record<string, string>, expectedFile: File) => { // 验证普通参数 Object.entries(expectedParams).forEach(([key, value]) => { expect(formData.get(key)).toBe(value) }) // 验证文件 expect(formData.get('file')).toBe(expectedFile) } // 修改测试代码 const myParams = { a: 'a', b: 'b' } await submitform(postUrl, file, myParams) expect(fetch).toHaveBeenCalledTimes(1) // 获取fetch的调用参数 const [callUrl, callOptions] = fetch.mock.calls[0] // 验证URL和请求配置 expect(callUrl).toBe("/url") expect(callOptions).toMatchObject({ method: 'post', headers: { authorization: 'Bearer abc' } }) // 验证FormData内容 validateFormData(callOptions.body as FormData, myParams, file)
方式2:直接捕获FormData并逐个验证
// 提前mock fetch global.fetch = jest.fn().mockResolvedValue({ json: jest.fn().mockResolvedValue({ success: true }) }) // 执行测试 const myParams = { a: 'a', b: 'b' } await submitform(postUrl, file, myParams) // 获取调用时的FormData const [, options] = fetch.mock.calls[0] const formData = options.body as FormData // 逐个验证内容 expect(formData.get('file')).toBe(file) expect(formData.get('a')).toBe('a') expect(formData.get('b')).toBe('b')
补充说明
日志中打印FormData {}是正常现象,因为FormData的默认toString()不会返回内部数据。如果需要查看FormData内容,可手动遍历:
// 正确打印FormData内容的方式 for (const [key, value] of formData.entries()) { console.log(`${key}: ${value}`) }
内容的提问来源于stack exchange,提问作者wick3d
相关产品推荐
相关产品推荐

