如何在NodeJS中模拟流

时间:2015-10-15 06:04:44

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

我试图对我的一个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()无效)。

我在溪流上找到的所有指导都使用了&#f;'或者' net'库作为创建新流(https://github.com/substack/stream-handbook)的基础,或者他们用sinon模拟它,但是模拟非常冗长非常快。

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

4 个答案:

答案 0 :(得分:12)

我应该使用“.emit(< event>,< data>);”

而不是使用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');

答案 1 :(得分:4)

接受答案只是部分正确。如果你只需要触发事件,使用.emit('data', datum)是可以的,但是如果你需要在任何其他地方管道这个模拟流,它将无法工作。

模拟可读流非常简单,只需要可读的lib。

  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);
      }
    }
  });

现在您可以在任何地方管道此流,并且“数据”和“结束”将会触发。

节点docs的另一个例子: https://nodejs.org/api/stream.html#stream_an_example_counting_stream

答案 2 :(得分:3)

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

我刚刚发现Node的stream.PassThrough类很容易错过,我相信这是您想要的。

来自Node文档:

  

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());

答案 3 :(得分:1)

在@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
  }

像魅力一样工作!