Jest结合React Testing Library mock fetch报错问题
Jest + React Testing Library 环境下fetch mock报错的解决方案
核心问题根因
所有TypeError: Cannot read property 'json' of undefined报错的本质,是测试执行时实际调用的全局fetch根本没有被替换为自定义mock,拿到的fetch返回值为undefined,导致后续调用.json()方法失败。常见的触发原因包括:
- 测试环境的全局变量被Jest的自动重置逻辑覆盖,自定义的fetch mock没有正确挂载到全局作用域
- jsdom版本过旧,本身没有内置fetch实现,也没有提前在全局层做polyfill
- 异步测试流程没有等待微任务队列清空,组件effect还没执行完就触发了断言,甚至让mock函数在fetch调用前就被重置
- mock返回的数据结构和被测代码的取值路径不匹配,就算mock生效也会引发后续属性读取错误
正确实现步骤
在全局测试入口初始化mock
在setupTests.js中统一挂载全局fetch,避免每个测试文件重复定义被环境重置:// src/setupTests.js global.fetch = jest.fn() // 每个测试用例执行前清空之前的mock状态,避免用例间污染 beforeEach(() => { fetch.mockClear() })如果使用
jest-fetch-mock工具,必须在这个文件里显式开启mock能力,仅安装依赖不会自动替换全局fetch:import fetchMock from 'jest-fetch-mock'; fetchMock.enableMocks();构造和业务逻辑完全匹配的mock返回结构
针对getPokemon函数的取值逻辑,mock返回的对象必须正确实现链式调用:先返回一个包含json方法的对象,再让json方法返回Promise resolve的宝可梦数据,不能把字段嵌套错层级:const mockPokemonDetail = { name: "charizard", sprites: { other: { "official-artwork": { front_default: "https://fake-static-url/charizard-art.png" } } } } fetch.mockResolvedValue({ json: jest.fn().mockResolvedValue(mockPokemonDetail) })注意:之前很多人写的
json: () => Promise.resolve({ data: { name: 'Charizard' } })是和被测代码逻辑不匹配的——被测代码是直接从json结果顶层取name、sprites字段,不需要额外套一层data,就算mock生效后续也会报读取sprites属性的错误。等待异步流程执行完成再做断言
测试React组件时,useEffect里触发的fetch是异步执行的,必须通过RTL提供的waitFor等待所有微任务(fetch请求、状态更新)跑完,不能在刚渲染完组件就做校验:test("组件加载完成后展示获取到的宝可梦信息", async () => { render(<Pokemon pokemonTrainer="ash" />) // 等待异步操作执行完成 await waitFor(() => { expect(screen.getByText("charizard")).toBeInTheDocument() }) // 校验fetch调用参数符合预期 expect(fetch).toHaveBeenCalledTimes(1) expect(fetch).toHaveBeenCalledWith( expect.stringContaining("https://pokeapi.co/api/v2/pokemon/") ) })
常见踩坑补充
- 不要在单个测试文件的顶层随意修改全局fetch,如果开启了Jest的
resetMocks配置,文件执行前会自动重置所有全局mock,导致fetch回到未定义状态 - 依赖注入方案失败的原因,通常是没有把自定义fetch作为参数显式传入
getPokemon函数,测试时实际还是调用了全局原生fetch,没有走到注入的mock逻辑
内容的提问来源于stack exchange,提问作者Kenny Chung
相关产品推荐
相关产品推荐

