如何在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或Node16jest.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
相关产品推荐
相关产品推荐

