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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 12:48:18