fs.createReadStream在Jest单元测试中无法触发事件问题
问题原因与解决方案
核心原因
Jest 默认在同步代码执行完毕后就会结束测试,而 fs.createReadStream 的 data/end 等事件是异步触发的。如果你的测试函数没有明确等待这些异步事件完成,Jest 会直接结束测试进程,导致事件回调根本没机会执行,所以只输出了同步的 "Program Ended"。
而直接用 ts-node 运行时,Node.js 的事件循环会一直运行到所有异步任务完成,所以流事件能正常触发。
解决方案
方案1:将流操作封装为 Promise(推荐)
修改 file_func.ts,把 working 函数改成返回 Promise,让测试可以通过 async/await 等待异步操作完成:
// file_func.ts import fs from 'fs'; import path from 'path'; export function working(filePath: string): Promise<string> { return new Promise((resolve, reject) => { // 用绝对路径避免Jest工作目录差异导致的文件找不到问题 const absolutePath = path.resolve(__dirname, filePath); const stream = fs.createReadStream(absolutePath, 'utf8'); let fullContent = ''; stream.on('data', (chunk) => { console.log('data'); fullContent += chunk; console.log(chunk); }); stream.on('end', () => { console.log('end'); resolve(fullContent); }); stream.on('error', (err) => { console.error('error', err); reject(err); }); }); } // 直接运行时的入口 if (require.main === module) { working('./test.txt') .then(() => console.log('Program Ended')) .catch(err => console.error(err)); }
对应的 Jest 测试代码:
// file_func.test.ts import { working } from './file_func'; test('read stream triggers data and end events', async () => { // 用await等待Promise完成,确保流事件都触发 const content = await working('./test.txt'); // 可选:添加断言验证文件内容正确性 expect(content).toBe('你的测试文件内容'); });
方案2:使用 Jest 的 done 回调(不修改原函数时用)
如果不想改动原函数,可以在测试中用 done 回调,手动等待事件触发完成:
// file_func.test.ts import { working } from './file_func'; test('read stream events trigger correctly', (done) => { let hasEndEvent = false; // 临时劫持console.log,判断end事件是否触发 const originalLog = console.log; console.log = (...args) => { if (args[0] === 'end') { hasEndEvent = true; } originalLog(...args); }; working('./test.txt'); // 轮询检查事件是否触发,确保异步操作完成 const checkInterval = setInterval(() => { if (hasEndEvent) { clearInterval(checkInterval); console.log = originalLog; done(); } }, 50); });
额外注意点
- 文件路径问题:Jest 运行时的工作目录可能和直接用
ts-node运行时不同,建议用path.resolve(__dirname, '文件名')生成绝对路径,避免因找不到文件触发error事件但未被捕获。 - 错误处理:测试中要确保
error事件也能被捕获,避免因文件不存在等问题导致测试卡住。
内容的提问来源于stack exchange,提问作者codo
相关产品推荐
相关产品推荐

