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

无法模拟global.fetch()函数的技术咨询

无法模拟global.fetch()函数的技术咨询

嘿,我看你在模拟global.fetch的时候碰钉子了,这种情况在测试依赖网络请求的代码时太常见了!我帮你梳理几个靠谱的解决思路,应该能帮你搞定。

先明确下场景:如果你的Evaluator类里注册的某个函数用到了fetch,那测试时直接调用evaluate或者evaluateFunction就会触发真实的网络请求,我们的目标就是把这个真实请求给模拟掉,返回预设的假数据,对吧?

方案1:用Jest的Mock功能(前端测试最常用的方案)

如果你的项目用Jest做测试框架,那直接用它内置的spyOn就可以轻松mockglobal.fetch:

// 举个完整的测试用例示例
import { Evaluator } from './evaluator';
import { FunctionRegistry } from './functionRegistry';

describe('Evaluator 测试', () => {
  let evaluator: Evaluator;
  let mockFetch: jest.SpyInstance;

  beforeEach(() => {
    // 先初始化函数注册器,假设我们有个依赖fetch的注册函数fetchData
    const funcRegistry = new FunctionRegistry();
    funcRegistry.register('fetchData', async () => {
      const response = await global.fetch('https://example.com/api/data');
      return response.json();
    });
    evaluator = new Evaluator(funcRegistry);

    // 开始模拟global.fetch
    mockFetch = jest.spyOn(global, 'fetch');
    // 给fetch预设一个假的返回值
    mockFetch.mockResolvedValue({
      json: jest.fn().mockResolvedValue({ id: 1, content: '测试数据' })
    } as Response);
  });

  afterEach(() => {
    // 重要!测试结束后恢复原有的fetch,避免影响其他测试用例
    mockFetch.mockRestore();
  });

  it('应该正确模拟fetch并返回预设数据', async () => {
    const testExpression = { type: 'function', name: 'fetchData', params: [] };
    const result = await evaluator.evaluate(testExpression);
    
    expect(result).toEqual({ id: 1, content: '测试数据' });
    expect(fetch).toHaveBeenCalledExactlyOnceWith('https://example.com/api/data');
  });
});

小提示:解决TypeScript类型报错

如果TypeScript抱怨global.fetch不存在类型定义,你可以在测试文件顶部加个全局类型声明:

declare global {
  namespace NodeJS {
    interface Global {
      fetch: typeof fetch;
    }
  }
}

临时救急也可以用(global as any).fetch,但还是推荐补全类型声明,代码更规范。

方案2:用Sinon.js做Stub(适合Mocha等其他测试框架)

如果你的项目用的是Mocha、Chai这类测试工具,那Sinon.js的stub功能很适合做这个:

import sinon from 'sinon';
import { Evaluator } from './evaluator';
import { FunctionRegistry } from './functionRegistry';

describe('Evaluator', () => {
  let evaluator: Evaluator;
  let fetchStub: sinon.SinonStub;

  beforeEach(() => {
    // 初始化注册器和evaluator,和上面一致
    const registry = new FunctionRegistry();
    registry.register('fetchData', async () => {
      const res = await global.fetch('https://example.com/data');
      return res.json();
    });
    evaluator = new Evaluator(registry);

    // 用Sinon stub住fetch
    fetchStub = sinon.stub(global, 'fetch').resolves({
      json: sinon.stub().resolves({ id: 1, content: '测试数据' })
    });
  });

  afterEach(() => {
    // 恢复原fetch
    fetchStub.restore();
  });

  it('应该通过Stub模拟fetch并返回正确结果', async () => {
    const expr = { type: 'function', name: 'fetchData', params: [] };
    const result = await evaluator.evaluate(expr);
    
    sinon.assert.calledOnce(fetchStub);
    expect(result).toEqual({ id: 1, content: '测试数据' });
  });
});

方案3:浏览器环境下的模拟方案

如果你的测试是在浏览器环境跑的(比如用Karma),那直接重写window.fetch就行:

// 保存原始的fetch,测试后恢复
const originalFetch = window.fetch;

beforeEach(() => {
  // 模拟fetch
  window.fetch = async () => ({
    json: async () => ({ id: 1, content: '测试数据' })
  });
});

afterEach(() => {
  // 恢复原fetch
  window.fetch = originalFetch;
});

// 测试用例写法类似上面

避坑提醒

  • 一定要记得在测试结束后恢复原始的fetch,不然其他测试用例可能会因为残留的mock出现莫名其妙的问题。
  • 所有涉及fetch的异步测试,必须用async/await或者.then()等待异步操作完成,不然断言会在请求结束前执行,导致测试失败。
  • 尽量别用any类型绕过TypeScript的检查,补全类型声明能帮你提前发现潜在问题。

备注:内容来源于stack exchange,提问作者Mert Aydın

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 07:38:11