Jest模拟node-fetch失效:控制器调用真实请求而非模拟请求
问题描述
我有一个使用node-fetch实现搜索功能的ProductController控制器,但在使用Jest模拟node-fetch时,控制器中的fetch仍会调用真实请求。以下是代码结构,请问模拟失效的原因是什么?该如何解决?
控制器代码
class ProductController { searchProduct = async (req: Request, resp: Response, next: NextFunction) => { const { name, category} = req.body const request = { name: name, category: category } const response = await fetch('https://tradvent.com/products/search', { method: 'post', body: JSON.stringify(request), headers: { 'Content-Type': 'application/json' }, }) const data: any = await response.json() return data } }
测试代码
jest.mock('node-fetch') import fetch from 'node-fetch'; describe('Product Search Suite', () =>{ it('test search', () =>{ const req = { name: 'abc', category: 'deluxe' } const response = { productCode: '123', category: 'deluxe', instock: true, ... } (fetch as jest.Mock).mockImplementation( () => Promise.resolve(new Response(JSON.stringify(response))) ); // calls the real fetch instead of fetch mock const data = await searchProduct(req) expect(data).toEqual(response) }
模拟失效的原因
- 控制器未显式导入
node-fetch:Node.js环境中fetch不是全局API,控制器中直接使用的fetch可能来自全局挂载或其他未被Jest mock的渠道,而jest.mock('node-fetch')仅对通过模块导入的fetch生效。 - 测试用例未声明为异步:测试回调未标记
async,但代码中使用了await,导致异步逻辑无法正确执行,测试流程混乱。 - 类方法调用错误:测试中直接调用
searchProduct(req),但该方法是ProductController的实例方法,未实例化类就调用会导致上下文错误,且无法关联到mock的fetch。 Response对象处理不当:测试中使用的Response若不是node-fetch提供的类,会导致mock返回的响应对象无法正常调用json()方法,进而影响测试结果。
修复方案
步骤1:修改控制器,显式导入node-fetch
确保控制器通过模块导入fetch,让Jest的mock能够覆盖:
import fetch from 'node-fetch'; import { Request, Response, NextFunction } from 'express'; class ProductController { searchProduct = async (req: Request, resp: Response, next: NextFunction) => { const { name, category } = req.body; const request = { name, category }; const response = await fetch('https://tradvent.com/products/search', { method: 'post', body: JSON.stringify(request), headers: { 'Content-Type': 'application/json' }, }); const data: any = await response.json(); return data; }; } export default ProductController;
步骤2:修正测试代码
解决异步标记、实例化类、正确mock响应等问题:
import ProductController from './ProductController'; import fetch from 'node-fetch'; jest.mock('node-fetch'); describe('Product Search Suite', () => { it('test search', async () => { // 构造正确的req对象(控制器读取req.body) const req = { body: { name: 'abc', category: 'deluxe' } }; const mockResponseData = { productCode: '123', category: 'deluxe', instock: true }; // 简化mock逻辑,直接模拟json()返回值 (fetch as jest.Mock).mockResolvedValue({ json: jest.fn().mockResolvedValue(mockResponseData) }); // 实例化控制器并调用方法 const controller = new ProductController(); const data = await controller.searchProduct(req as any, {} as any, jest.fn()); // 断言结果与请求参数 expect(data).toEqual(mockResponseData); expect(fetch).toHaveBeenCalledWith( 'https://tradvent.com/products/search', expect.objectContaining({ method: 'post', body: JSON.stringify({ name: 'abc', category: 'deluxe' }), headers: { 'Content-Type': 'application/json' } }) ); }); });
内容的提问来源于stack exchange,提问作者BreenDeen
相关产品推荐
相关产品推荐

