Node18+TypeScript环境下Nock无法拦截Fetch请求的解决方法
Nock无法拦截Node.js 18原生fetch请求,真实接口被调用
问题背景
我在Node.js 18的TypeScript项目中使用Nock测试一个通过原生fetch调用接口的MagicClient类,但Nock完全失效,真实接口被调用,返回真实响应而非Mock数据。
客户端代码(MagicClient.ts)
type Config = { baseUrl: string; }; export default class MagicClient { // eslint-disable-next-line no-useless-constructor constructor(private readonly config: Config) {} public async doMagic(): Promise<Object> { const response = await fetch(`${this.config.baseUrl}/do-magic`, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ magic: true }), }); return JSON.parse(await response.text()); } }
测试代码(MagicClient.test.ts)
import nock from 'nock'; import MagicClient from '../../../src/MagicClient'; describe(MagicClient.name, () => { const client = new MagicClient({ baseUrl: process.env.MAGIC_BASE_URL, }); beforeEach(() => { jest.resetAllMocks(); nock.cleanAll(); nock.disableNetConnect(); nock.enableNetConnect('127.0.0.1'); }); afterEach(() => { expect(nock.isDone()).toBeTruthy(); }); const mockedResponse = { success: true, magic: 'abc', }; it('calls do magic', () => { nock(process.env.MAGIC_BASE_URL) .post('/do-magic', { magic: true }) .reply(200, mockedResponse); expect( client.doMagic(), ).toEqual(mockedResponse); }); });
环境信息
node v18.16.0 "jest": "^29.2.0", "nock": "^13.4.0",
问题原因
- Nock默认不支持Node.js 18原生fetch:Node.js 18的
fetch基于undici库实现,而Nock默认只拦截传统http/https模块的请求,无法直接拦截undici驱动的fetch调用。 - 测试未正确处理异步逻辑:
doMagic是异步函数,测试中未使用await等待请求完成,导致expect在请求发送前就执行,Nock的拦截规则还未生效,真实请求已发出。 - 主机名配置可能不匹配:如果
process.env.MAGIC_BASE_URL包含协议(如https://),Nock需要完整匹配该地址;若传递格式错误,拦截规则会不匹配,请求会绕过Nock。
解决方案
1. 启用Nock对原生fetch的支持
安装Nock的fetch适配包:
npm install @nock/fetch --save-dev
在测试文件顶部导入该包,替换原生fetch:
import '@nock/fetch'; // 放在nock导入之前 import nock from 'nock';
也可以在Jest全局配置文件(如jest.setup.js)中导入,避免每个测试文件重复配置。
2. 修复测试的异步处理
将测试函数改为异步函数,并在expect前添加await,确保等待请求完成后再断言:
it('calls do magic', async () => { // 添加async关键字 nock(process.env.MAGIC_BASE_URL) .post('/do-magic', { magic: true }) .reply(200, mockedResponse); const result = await client.doMagic(); // 添加await等待请求完成 expect(result).toEqual(mockedResponse); });
3. 验证主机名配置
确保nock的第一个参数与baseUrl完全匹配(包括协议)。可通过URL对象提取标准格式的主机地址:
const baseUrl = new URL(process.env.MAGIC_BASE_URL); nock(baseUrl.origin) // 使用origin确保包含协议和主机名 .post('/do-magic', { magic: true }) .reply(200, mockedResponse);
4. 强化网络拦截规则
在beforeEach中严格禁用外部网络,避免请求绕过Nock:
beforeEach(() => { jest.resetAllMocks(); nock.cleanAll(); nock.disableNetConnect(); // 仅允许本地回环地址的请求 nock.enableNetConnect('127.0.0.1', 'localhost'); });
修改后的完整测试代码
import '@nock/fetch'; import nock from 'nock'; import MagicClient from '../../../src/MagicClient'; describe(MagicClient.name, () => { const client = new MagicClient({ baseUrl: process.env.MAGIC_BASE_URL, }); beforeEach(() => { jest.resetAllMocks(); nock.cleanAll(); nock.disableNetConnect(); nock.enableNetConnect('127.0.0.1', 'localhost'); }); afterEach(() => { expect(nock.isDone()).toBeTruthy(); }); const mockedResponse = { success: true, magic: 'abc', }; it('calls do magic', async () => { const baseUrl = new URL(process.env.MAGIC_BASE_URL); nock(baseUrl.origin) .post('/do-magic', { magic: true }) .reply(200, mockedResponse); const result = await client.doMagic(); expect(result).toEqual(mockedResponse); }); });
内容的提问来源于stack exchange,提问作者Kristi Jorgji
相关产品推荐
相关产品推荐

