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

Jest模拟fetch报TypeError:无法读取undefined的then属性

业务代码实现

编写的get方法通过fetch请求本地JSON文件并返回解析后数据,实现代码如下:

// eslint-disable-next-line import/prefer-default-export
export const get = () => {
  // eslint-disable-next-line no-undef
  return fetch('data/posts.json').then((res) => res.json());
};

初始测试代码

Jest测试文件中编写的全局fetch mock与测试用例如下:

import { get } from './posts';

const mockData = [
  {
    "id": "ig-1",
    "accountId": "IG",
    "accountIcon": "/images/ig-icon.svg",
    "accountName": "IG account",
    "accountImageInitial": "J",
    "imageUrl": "/images/social_logo.png",
    "caption": "test",
    "timestamp": 1635510651638
  },
  {
    "id": "fb-1",
    "accountId": "FB",
    "accountIcon": "/images/fb-icon.svg",
    "accountName": "FB account",
    "accountImageInitial": "J",
    "imageUrl": "/images/social_logo.png",
    "caption": "test",
    "timestamp": 1635510051638
  }
];

global.fetch = jest.fn(() => 
  Promise.resolve({
    json: () => Promise.resolve(mockData)
  })
);

describe('The posts API controller', () => {
  test('get() returns expected default payload', async () => {
    const result = await get();
    expect(fetch).toHaveBeenCalledTimes(1);
    expect(result).toBeTruthy();
  });
});

报错信息

运行测试时抛出如下错误:

TypeError: Cannot read properties of undefined (reading 'then')

对应的错误栈指向fetch调用行:

TypeError: Cannot read properties of undefined (reading 'then')

      2 | export const get = () => {
      3 |   // eslint-disable-next-line no-undef
    > 4 |   return fetch('data/posts.json').then((res) => res.json());
        |          ^
      5 | };

错误截图:
错误截图

问题根因

报错核心原因是ES模块导入提升导致mock挂载时序错误:
ES模块规范下,所有import语句会被JS引擎提升到当前文件的最顶部优先执行,和代码书写位置无关。测试代码实际执行顺序为:

  1. 优先执行import { get } from './posts'加载posts模块,此时Node环境默认无全局fetch,且mock赋值逻辑还未执行,模块内捕获到的全局fetch为undefined
  2. 之后才执行global.fetch = jest.fn(...)赋值逻辑,但此时posts模块已经拿到了undefined的fetch引用
  3. 测试用例调用get()时,内部执行undefined.then(...)直接抛出类型错误
修复方案

使用Jest官方推荐的spyOn方式在测试初始化钩子里挂载mock,既解决时序问题,也方便测试后还原全局对象避免污染其他用例:

import { get } from './posts';

const mockData = [
  {
    "id": "ig-1",
    "accountId": "IG",
    "accountIcon": "/images/ig-icon.svg",
    "accountName": "IG account",
    "accountImageInitial": "J",
    "imageUrl": "/images/social_logo.png",
    "caption": "test",
    "timestamp": 1635510651638
  },
  {
    "id": "fb-1",
    "accountId": "FB",
    "accountIcon": "/images/fb-icon.svg",
    "accountName": "FB account",
    "accountImageInitial": "J",
    "imageUrl": "/images/social_logo.png",
    "caption": "test",
    "timestamp": 1635510051638
  }
];

describe('The posts API controller', () => {
  // 每个用例执行前挂载mock
  beforeEach(() => {
    jest.spyOn(global, 'fetch').mockResolvedValue({
      json: jest.fn().mockResolvedValue(mockData)
    });
  });

  // 每个用例执行后还原所有mock,清理全局污染
  afterEach(() => {
    jest.restoreAllMocks();
  });

  test('get() returns expected default payload', async () => {
    const result = await get();
    expect(fetch).toHaveBeenCalledTimes(1);
    // 替换原有弱断言,直接校验返回值和mock数据完全一致
    expect(result).toEqual(mockData);
  });
});

额外配置说明:如果使用Jest 28及以上版本,默认测试环境为node,本身不提供浏览器全局API,需要在jest配置文件中添加testEnvironment: "jsdom",模拟浏览器环境后再测试fetch相关逻辑。

内容的提问来源于stack exchange,提问作者Leon Gaban

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 00:48:24