【问题标题】:How to mock streams in NodeJS如何在 NodeJS 中模拟流
【发布时间】:2016-01-13 11:12:49
【问题描述】:

我正在尝试对我的一个 node-js 模块进行单元测试,该模块主要处理流。我正在尝试模拟一个流(我将写入),因为在我的模块中我有我想触发的“.on('data/end)”侦听器。基本上我希望能够做这样的事情:

var mockedStream = new require('stream').readable();

mockedStream.on('data', function withData('data') {
  console.dir(data);
});

mockedStream.on('end', function() { 
  console.dir('goodbye');
});

mockedStream.push('hello world');
mockedStream.close();

这会执行,但在我执行推送后,'on' 事件永远不会被触发(并且 .close() 无效)。

我可以在流上找到的所有指导都使用“fs”或“net”库作为创建新流的基础 (https://github.com/substack/stream-handbook),或者他们用 sinon 模拟它,但模拟变得非常冗长非常快.

有没有很好的方法来提供这样的虚拟流?

【问题讨论】:

    标签: node.js unit-testing node.js-stream


    【解决方案1】:

    有一个更简单的方法:stream.PassThrough

    我刚刚发现 Node 很容易错过 stream.PassThrough 课程,我相信这就是您要找的。​​p>

    来自节点文档:

    stream.PassThrough 类是 Transform 流的一个简单实现,它只是将输入字节传递到输出。 它的目的主要是为了示例和测试...

    link to docs

    问题中的代码,已修改:

    const { PassThrough } = require('stream');
    const mockedStream = new PassThrough(); // <----
    
    mockedStream.on('data', (d) => {
        console.dir(d);
    });
    
    mockedStream.on('end', function() {
        console.dir('goodbye');
    });
    
    mockedStream.emit('data', 'hello world');
    mockedStream.end();   //   <-- end. not close.
    mockedStream.destroy();
    

    mockedStream.push() 也可以,但作为Buffer,你可能想要这样做:console.dir(d.toString());

    【讨论】:

      【解决方案2】:

      我应该使用“.emit(, );”而不是使用 Push

      我的模拟代码现在可以运行并且看起来像:

      var mockedStream = new require('stream').Readable();
      mockedStream._read = function(size) { /* do nothing */ };
      
      myModule.functionIWantToTest(mockedStream); // has .on() listeners in it
      
      mockedStream.emit('data', 'Hello data!');
      mockedStream.emit('end');
      

      【讨论】:

      • 更正:new require('stream').Readable()(注意大写 R)
      • require 不是构造函数,这将在没有 new 关键字的情况下工作
      • @Spets require 不是这里的构造函数,Readable
      【解决方案3】:

      接受的答案只是部分正确。如果您只需要触发事件,则使用.emit('data', datum) 是可以的,但如果您需要将此模拟流通过管道传输到其他任何地方,它将无法正常工作。

      模拟 Readable 流非常容易,只需要 Readable 库。

        let eventCount = 0;
        const mockEventStream = new Readable({
          objectMode: true,
          read: function (size) {
            if (eventCount < 10) {
              eventCount = eventCount + 1;
              return this.push({message: `event${eventCount}`})
            } else {
              return this.push(null);
            }
          }
        });
      

      现在您可以在任何地方通过管道传输此流,并且“data”和“end”将触发。

      来自节点文档的另一个示例: https://nodejs.org/api/stream.html#stream_an_example_counting_stream

      【讨论】:

        【解决方案4】:

        在@flacnut 的回答的基础上,我使用Readable.from() 来构建一个预加载数据的流(文件名列表)(在NodeJS 12+ 中):

            const mockStream = require('stream').Readable.from([
               'file1.txt',
               'file2.txt',
               'file3.txt',
            ])
        

        就我而言,我想模拟 fast-glob.stream 返回的文件名流:

            const glob = require('fast-glob')
            // inject the mock stream into glob module
            glob.stream = jest.fn().mockReturnValue(mockStream)
        
        

        在被测试的函数中:

          const stream = glob.stream(globFilespec)
          for await (const filename of stream) {
            // filename = file1.txt, then file2.txt, then file3.txt
          }
        
        

        像魅力一样工作!

        【讨论】:

          猜你喜欢
          • 2017-11-09
          • 2018-12-05
          • 1970-01-01
          • 2023-03-30
          • 1970-01-01
          • 1970-01-01
          • 2018-07-19
          • 1970-01-01
          • 2020-11-26
          相关资源
          最近更新 更多