在Jest中模拟带withFileTypes的fs.readdirSync遇错误如何解决?
问题描述
我有一个通过fs.readdirSync获取目录文件名的函数:
export const getDirectoryFiles = async (directory) => { return fs .readdirSync(directory, { withFileTypes: true }) .filter(dirent => !dirent.isDirectory()) .map(dirent => dirent.name); }
尝试用Jest的jest.spyOn模拟该方法时,测试报错:
TypeError: fs.readdirSync(...).filter is not a function
我的测试代码如下:
it('getDirectoryFiles', async () => { const { Dirent, constants } = jest.requireActual('fs'); // Prepare our mock const directory = new Dirent('directory', constants.UV_DIRENT_DIR); const fsMock = jest.spyOn(fs, 'readdirSync'); fsMock.mockResolvedValue([directory]); // expect.assertions(1); const files = await getDirectoryFiles("/dir/stuff"); // quick test expect(files).not.toBeNull; });
希望解决报错问题,同时模拟返回两个文件名来验证函数的正确性。
解决方案
1. 修复同步方法的模拟方式
fs.readdirSync是同步方法,你用的mockResolvedValue是用来模拟异步Promise返回值的,这会导致readdirSync返回Promise而非数组,自然没有filter方法——这就是报错的核心原因。
把mockResolvedValue替换为mockReturnValue即可解决类型错误。
2. 模拟符合要求的Dirent实例
你需要创建文件类型的Dirent对象(而非目录类型),这样filter逻辑才会保留它们,最终映射出正确的文件名。无需依赖真实的Dirent类,手动模拟必要的方法和属性即可满足测试需求。
完整测试代码
it('getDirectoryFiles should return correct filenames', async () => { // 模拟两个文件的Dirent对象 const mockFile1 = { isDirectory: () => false, name: 'file1.txt' }; const mockFile2 = { isDirectory: () => false, name: 'file2.js' }; // 模拟一个目录,验证是否会被过滤 const mockDir = { isDirectory: () => true, name: 'subdir' }; const fsMock = jest.spyOn(fs, 'readdirSync'); // 同步方法用mockReturnValue返回数组 fsMock.mockReturnValue([mockFile1, mockFile2, mockDir]); const files = await getDirectoryFiles("/dir/stuff"); // 验证结果只包含文件,不包含目录 expect(files).toEqual(['file1.txt', 'file2.js']); // 验证readdirSync被正确调用 expect(fsMock).toHaveBeenCalledWith("/dir/stuff", { withFileTypes: true }); });
额外说明
- 你的
getDirectoryFiles函数标记了async,但内部用的是同步方法readdirSync,实际上可以去掉async,不过这不会影响测试逻辑。 - 无需引入真实的
Dirent类,模拟isDirectory方法和name属性就足够覆盖函数的过滤、映射逻辑。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

