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

如何在Jest中模拟Node.js的非全局变量__filename和__dirname?

在Jest中模拟__filename和__dirname的可行策略

__filename和__dirname是Node.js通过模块包装函数注入的特殊变量,不属于global对象,因此直接通过globalThis/global赋值的方式无法在Jest中模拟它们。以下是几种可行的策略,以及是否需要接受限制的分析:

一、理想方案:使用jest.mock重写模块(无业务代码侵入)

通过手动模拟目标模块的导出,直接构造包含自定义路径信息的返回值,完全在测试层实现隔离,不需要修改业务代码。

示例测试代码(utils.test.ts):

// 保留模块其他导出,仅重写目标函数
jest.mock('./utils', () => {
  const originalModule = jest.requireActual('./utils');
  return {
    ...originalModule,
    getFileAndDirName: jest.fn(() => ({
      relevantFile: 'utils',
      rootPath: '/Users/me/work/utils'
    }))
  };
});

import { getFileAndDirName } from './utils';

it('返回正确的文件和目录信息', () => {
  expect(getFileAndDirName()).toEqual({
    relevantFile: 'utils',
    rootPath: '/Users/me/work/utils'
  });

  // 动态修改mock返回值,模拟不同路径场景
  (getFileAndDirName as jest.Mock).mockReturnValue({
    relevantFile: 'tests',
    rootPath: '/Users/me/work/tests'
  });
  expect(getFileAndDirName()).toEqual({
    relevantFile: 'tests',
    rootPath: '/Users/me/work/tests'
  });
});

二、非理想但灵活的方案:自定义模块包装函数

通过Jest的全局配置修改模块包装逻辑,注入自定义的__filename和__dirname,但需要处理模块缓存问题。

  1. 修改jest.config.js:
module.exports = {
  setupFiles: ['./jest.setup.js']
};
  1. 编写jest.setup.js:
// 覆盖默认的模块包装函数,支持全局自定义路径变量
jest.mock('module', () => {
  const originalModule = jest.requireActual('module');
  return {
    ...originalModule,
    wrapper: (exports, require, module, __filename, __dirname) => {
      const customFilename = global.__CUSTOM_FILENAME__ || __filename;
      const customDirname = global.__CUSTOM_DIRNAME__ || __dirname;
      return originalModule.wrapper(exports, require, module, customFilename, customDirname);
    }
  };
});
  1. 测试文件中使用:
it('全局模拟__filename和__dirname', () => {
  global.__CUSTOM_FILENAME__ = '/Users/me/work/utils/index.ts';
  global.__CUSTOM_DIRNAME__ = '/Users/me/work/utils';
  
  // 重置模块缓存,确保新的路径变量生效
  jest.resetModules();
  const { getFileAndDirName } = require('./utils');
  expect(getFileAndDirName()).toEqual({
    relevantFile: 'utils',
    rootPath: '/Users/me/work/utils'
  });

  // 切换路径场景
  global.__CUSTOM_FILENAME__ = '/Users/me/work/tests/index.ts';
  global.__CUSTOM_DIRNAME__ = '/Users/me/work/tests';
  jest.resetModules();
  const { getFileAndDirName: updatedGetInfo } = require('./utils');
  expect(updatedGetInfo()).toEqual({
    relevantFile: 'tests',
    rootPath: '/Users/me/work/tests'
  });
});

三、保守方案:接受限制并规避

如果不想引入复杂的测试配置,或项目对测试复杂度有严格要求,可以选择以下方式规避:

  • 提取依赖路径的纯逻辑:将__filename.split('/')?.[3]这类路径处理逻辑单独拆分为纯函数,仅测试纯函数的正确性,而__filename/__dirname的注入部分依赖Node.js原生行为,不做单元测试。
  • 集成测试验证:在实际文件路径下运行集成测试,验证整体逻辑的正确性,跳过单元级的变量模拟。

总结

优先选择jest.mock重写模块的方案,它既不侵入业务代码,又能保证测试隔离性;如果需要全局范围的模拟,可以尝试自定义模块包装函数的方案,但需注意模块缓存的影响;若项目场景简单,接受限制并调整测试策略也是合理的选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 11:10:38