无法模拟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
相关产品推荐
相关产品推荐

