如何使用Sinon对cross-fetch实现stub mock单元测试
使用Sinon mock cross-fetch验证调用参数的实现方案
直接给出适配你业务代码的测试实现:
import * as sinon from 'sinon'; import * as fetchModule from 'cross-fetch'; import { MyService } from './你的服务文件路径'; // 替换为你自己的服务类文件路径 import { expect } from 'chai'; // 用其他断言库可替换为对应导入 describe('MyService 逻辑测试', () => { let fetchStub; let serviceInstance; beforeEach(() => { // Stub cross-fetch的默认导出,模拟请求成功返回 fetchStub = sinon.stub(fetchModule, 'default').resolves({ ok: true, json: () => Promise.resolve({}), text: () => Promise.resolve('') }); // 初始化服务实例 serviceInstance = new MyService(); // 可选:如果需要固定generatePayload的返回值,可单独stub该方法 sinon.stub(serviceInstance, 'generatePayload').resolves({ foo: 'bar', count: 1 }); }); afterEach(() => { // 恢复所有stub,避免污染其他测试用例 sinon.restore(); }); it('sendMyData 方法调用时应该传递正确的payload到cross-fetch', async () => { const testId = 'test_001'; const expectPayload = { foo: 'bar', count: 1 }; // 执行待测试方法 await serviceInstance.sendMyData(testId); // 1. 验证fetch仅被调用1次 sinon.assert.calledOnce(fetchStub); // 2. 验证调用URL和请求参数符合预期 sinon.assert.calledWithExactly(fetchStub, `http://my-api-url/endpoint/${testId}`, { method: 'POST', body: JSON.stringify(expectPayload) } ); // 如果需要对payload做更灵活的校验,可以取出实际调用参数解析后判断 const actualPayload = JSON.parse(fetchStub.firstCall.args[1].body); expect(actualPayload.foo).to.equal('bar'); expect(actualPayload.count).to.equal(1); }); });
为什么要通过
import * as fetchModule的方式导入cross-fetch?因为ESM规范下默认导出是模块的default属性,只有将整个模块作为命名空间导入,才能通过Sinon stub修改default属性的指向,实现mock效果。
适配说明
- 如果你项目使用CommonJS规范(用
require导入模块),不需要加.default,直接stubfetchModule本身即可 - 如果需要模拟fetch请求失败的场景,将stub的返回改为
.rejects(new Error('请求失败'))即可测试异常分支逻辑 - 如果你不需要固定
generatePayload的返回,直接删除对应stub代码即可,测试会走真实的generatePayload逻辑,同样能校验最终传给fetch的payload是否正确
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

