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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 02:35:29