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

如何在Jest中正确Mock fs/promises并验证函数调用?

解决Jest Mock fs/promises.readdir 未被调用的问题

几个核心排查点

1. 先Mock再导入模块!顺序错了全白搭

Jest的mock必须在导入你要测试的模块之前完成,不然目标模块会先加载原生的fs/promises,后面的mock根本不会生效。

反例(千万别这么写):

// 先导入目标模块,再mock,等于白做
import { evaluateTrades } from './tradeEvaluator';
jest.mock('fs/promises', () => ({
  readdir: jest.fn().mockResolvedValue(['trade1.json', 'trade2.json'])
}));

正确姿势:

// 先把mock搞定,再导入要测的模块
jest.mock('fs/promises', () => ({
  readdir: jest.fn().mockResolvedValue(['trade1.json', 'trade2.json'])
}));
import { evaluateTrades } from './tradeEvaluator';

2. 目标模块的导入方式要和Mock结构匹配

如果你的tradeEvaluator.ts是用解构导入readdir的,那mock的结构必须完全对应:

比如目标模块代码:

import { readdir } from 'fs/promises';

export async function evaluateTrades(dirPath: string) {
  const files = await readdir(dirPath);
  // ... 业务逻辑
}

那mock就得直接导出readdir这个方法,不能瞎改结构:

// 正确写法
jest.mock('fs/promises', () => ({
  readdir: jest.fn().mockResolvedValue(['file1.json'])
}));

// 错误写法(解构导入的话完全没用)
jest.mock('fs/promises', () => jest.fn().mockReturnValue({ readdir: ... }));

3. TypeScript配置别拖后腿

检查tsconfig.json和jest.config.ts的配置,确保两者兼容:

  • tsconfig.json里的module设为CommonJS或ESNext,moduleResolution设为NodeNext或Node16
  • jest.config.ts要开启esModuleInterop和allowSyntheticDefaultImports,避免ES模块和CommonJS的适配问题

示例jest.config.ts:

export default {
  preset: 'ts-jest',
  testEnvironment: 'node',
  moduleFileExtensions: ['ts', 'js'],
  transform: {
    '^.+\\.ts$': 'ts-jest'
  },
  globals: {
    'ts-jest': {
      tsconfig: 'tsconfig.json'
    }
  },
  esModuleInterop: true,
  allowSyntheticDefaultImports: true
};

4. 先确认Mock真的被用上了

可以在测试里加个小验证,看看readdir是不是真的指向Jest的mock函数:

import { readdir } from 'fs/promises';

test('检查mock是否生效', () => {
  console.log(readdir); // 正常应该输出类似 [Function: mockConstructor]
  expect(jest.isMockFunction(readdir)).toBe(true);
});

5. 用jest.spyOn的话别写错对象

如果习惯用spyOn来mock,要注意直接针对fs/promises这个对象,别绕弯路:

// 正确写法
import * as fs from 'fs/promises';

jest.spyOn(fs, 'readdir').mockResolvedValue(['file.json']);

// 错误写法(会导致mock失效,别这么搞)
jest.spyOn(fs.promises, 'readdir'); // 这里fs.promises是重复引用,等于白忙活

完整可运行测试示例

// 先mock,再导入模块
jest.mock('fs/promises', () => ({
  readdir: jest.fn().mockResolvedValue(['trade-2024-01.json', 'trade-2024-02.json'])
}));

import { evaluateTrades } from './tradeEvaluator';
import { readdir } from 'fs/promises';

describe('tradeEvaluator', () => {
  test('应该正确调用readdir', async () => {
    const testDir = './test-trades';
    await evaluateTrades(testDir);
    
    expect(readdir).toHaveBeenCalledTimes(1);
    expect(readdir).toHaveBeenCalledWith(testDir);
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 16:15:00