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

如何测试柯里化函数中作为参数传入的非直接调用Mock方法?

解决Jest无法捕获柯里化Logger中console调用的问题

这个问题的核心原因其实很简单:你的logger模块在加载时就已经绑定了原始的console.log/console.info引用,而Jest的spyOn是在测试运行时才替换console方法的,这时候柯里化函数里保存的还是模块加载时的旧引用,自然测不到调用。

下面给你三种可行的解决方案,你可以根据自己的场景选择:

方案一:延迟获取console方法引用

修改logger的实现,让它每次调用日志方法时才去获取当前的console方法,而不是提前缓存引用:

// logger.ts
// 把logFn改成一个返回函数的 getter,每次调用都取最新值
const log = (getLogFn: () => (...args: any[]) => void) => (message: any) => {
  getLogFn()(message);
};

const logger = {
  debug: log(() => console.log),
  info: log(() => console.info),
};

export default logger;

这样每次调用logger.debug()时,都会重新获取console.log的当前值,Jest的spy就能正常捕获到调用了。

方案二:测试前重置模块缓存并重新导入

利用Jest的模块缓存机制,在创建spy之后再重新加载logger模块,让它绑定被spy替换后的console方法:

// logger.spec.ts
describe('logger', () => {
  describe('debug', () => {
    it('sets a text as log output', () => {
      // 先创建spy并替换console.log
      const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
      
      // 清除已加载的logger模块缓存
      jest.resetModules();
      // 在spy生效后重新导入logger
      const logger = require('./logger').default;
      
      const text = 'Export is working properly';
      logger.debug(text);
      
      expect(consoleSpy).toHaveBeenCalledTimes(1);
      expect(consoleSpy).toHaveBeenCalledWith(text);
      
      // 恢复原始console.log
      consoleSpy.mockRestore();
    });
  });
});

这种方法不需要修改业务代码,适合不想改动原有logger实现的场景。

方案三:使用依赖注入(最推荐)

把console作为依赖注入到logger中,这样测试时可以直接传入mock的console实例,完全不需要依赖Jest的spy机制,测试逻辑更清晰:

// logger.ts
// 定义一个工厂函数,允许传入自定义的console实例
const createLogger = (consoleInstance = console) => {
  const log = (logFn: (...args: any[]) => void) => (message: any) => {
    logFn(message);
  };
  
  return {
    debug: log(consoleInstance.log),
    info: log(consoleInstance.info),
  };
};

// 默认导出使用原生console的logger
export default createLogger();
// 导出工厂函数供测试使用
export { createLogger };

然后测试代码可以这样写:

// logger.spec.ts
import { createLogger } from './logger';

describe('logger', () => {
  describe('debug', () => {
    it('sets a text as log output', () => {
      // 创建一个mock的console对象
      const mockConsole = {
        log: jest.fn(),
        info: jest.fn(),
      };
      
      // 使用mock console创建logger实例
      const logger = createLogger(mockConsole);
      
      const text = 'Export is working properly';
      logger.debug(text);
      
      expect(mockConsole.log).toHaveBeenCalledTimes(1);
      expect(mockConsole.log).toHaveBeenCalledWith(text);
    });
  });
});

这种方式不仅解决了测试问题,还让logger的扩展性更好——以后如果需要替换日志实现(比如输出到文件),只需要传入对应的console-like对象即可。

内容的提问来源于stack exchange,提问作者axel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 21:13:11