web-dev-qa-db-ja.com

NodeJSでストリームをモックする方法

ストリームを大量に処理するnode-jsモジュールの1つをユニットテストしようとしています。モジュール内にトリガーしたい「.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 )、または彼らはそれをシノンでモックアウトしますが、モックは非常に長く非常に速くなります。

このようなダミーストリームを提供する良い方法はありますか?

9
flacnut

プッシュを使用する代わりに、「。emit(<event>、<data>);」を使用する必要がありました。

私のモックコードは機能し、次のようになります。

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');
14
flacnut

受け入れの答えは部分的にしか正しくありません。発生するイベントだけが必要な場合は、.emit('data', datum)を使用しても問題ありませんが、このモックストリームを他の場所にパイプする必要がある場合は、機能しません。

読み取り可能なストリームのモックは驚くほど簡単で、読み取り可能なライブラリのみが必要です。

  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

5
Julius Ecker

@flacnutの回答に基づいて、(NodeJS 12以降で)Readable.from()を使用してこれを行い、データ(ファイル名のリスト)がプリロードされたストリームを構築しました。

    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
  }

チャームのように機能します!

1
dthorpe

もっと簡単な方法があります:stream.PassThrough

Nodeのstream.PassThroughクラスを見逃しやすいことがわかりました。これは、あなたが探しているものだと思います。

From Node docs:

Stream.PassThroughクラスは、入力バイトを出力に渡すだけのTransformストリームの簡単な実装です。 その目的は主に例とテストのためです...

ドキュメントへのリンク

質問のコードを変更:

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

1
Taitu-lism