web-dev-qa-db-ja.com

TJS / NodeJSのストリームを使用したテスト

私はストリームを使用するコードをテストするための合理的な方法を見つけようとしてきました。 nodejsでストリームを使用するコードのテストに役立つ適切な方法/フレームワークを見つけた人はいますか?

例えば:

var fs = require('fs'),
    request = require('request');

module.exports = function (url, path, callback) {
  request(url)
    .pipe(fs.createWriteStream(path))
    .on('finish', function () {
      callback();
    });
};

このタイプのコードをテストする私の現在の方法は、ストリームを使用してコードを単純化して、テストされていないコードの塊に抽象化するか、次のようなものを書くことです。

var rewire = require('rewire'),
    download = rewire('../lib/download'),
    stream = require('stream'),
    util = require('util');

describe('download', function () {
  it('should download a url', function (done) {
    var fakeRequest, fakeFs, FakeStream;

    FakeStream = function () {
      stream.Writable.call(this);
    };

    util.inherits(FakeStream, stream.Writable);

    FakeStream.prototype._write = function (data, encoding, cb) {
      expect(data.toString()).toEqual("hello world")
      cb();
    };

    fakeRequest = function (url) {
      var output = new stream.Readable();

      output.Push("hello world");
      output.Push(null);

      expect(url).toEqual('http://hello');

      return output;
    };

    fakeFs = {
      createWriteStream: function (path) {
        expect(path).toEqual('hello.txt');
        return new FakeStream();
      }
    };

    download.__set__('fs', fakeFs);
    download.__set__('request', fakeRequest);

    download('http://hello', 'hello.txt', function () {
      done();
    });

  });
});

誰かがストリームをテストするよりエレガントな方法を考え出しましたか?

35
Michael Wasser

その目的のためにストリームテストを行いました。ストリームテストをよりクリーンにするだけでなく、V1およびV2ストリームをテストすることもできます https://www.npmjs.com/package/streamtest

14
nfroidure

memorystream も使用していますが、アサーションをfinishイベントに入れています。そうすれば、テストされているストリームを実際に使用したように見えます。

require('chai').should();

var fs = require('fs');
var path = require('path');

var MemoryStream = require('memorystream');
var memStream = MemoryStream.createWriteStream();

/**
 * This is the Transform that we want to test:
 */

var Parser = require('../lib/parser');
var parser = new Parser();

describe('Parser', function(){
  it('something', function(done){
    fs.createReadStream(path.join(__dirname, 'something.txt'))
      .pipe(parser)
      .pipe(memStream)
      .on('finish', function() {

        /**
         * Check that our parser has created the right output:
         */

        memStream
          .toString()
          .should.eql('something');
        done();
      });
  });
});

オブジェクトのチェックは次のように行うことができます:

var memStream = MemoryStream.createWriteStream(null, {objectMode: true});
.
.
.
      .on('finish', function() {
        memStream
          .queue[0]
          .should.eql({ some: 'thing' });
        done();
      });
.
.
.
7
Mark Birbeck

ストリームをメモリに読み込み、予想されるバッファと比較します。

it('should output a valid Stream', (done) => {
  const stream = getStreamToTest();
  const expectedBuffer = Buffer.from(...);
  let bytes = new Buffer('');

  stream.on('data', (chunk) => {
    bytes = Buffer.concat([bytes, chunk]);
  });

  stream.on('end', () => {
    try {
      expect(bytes).to.deep.equal(expectedBuffer);
      done();
    } catch (err) {
      done(err);
    }
  });
});
4
Westy92

痛みを感じます。

ストリームのテストに役立つフレームワークはわかりませんが、ストリームライブラリを開発している here を見てみると、この問題への取り組み方がわかります。

ここに私がやっていることのアイデアがあります。

var chai = require("chai")
, sinon = require("sinon")
, chai.use(require("sinon-chai"))
, expect = chai.expect
, through2 = require('through2')
;

chai.config.showDiff = false

function spy (stream) {
  var agent, fn
  ;
  if (spy.free.length === 0) {
    agent = sinon.spy();
  } else {
    agent = spy.free.pop();
    agent.reset();
  }
  spy.used.Push(agent);
  fn = stream._transform;
  stream.spy = agent;
  stream._transform =  function(c) {
    agent(c);
    return fn.apply(this, arguments);
  };
  stream._transform = transform;
  return agent;
};

spy.free = [];
spy.used = [];


describe('basic through2 stream', function(){

  beforeEach(function(){
    this.streamA = through2()
    this.StreamB = through2.obj()
    // other kind of streams...

    spy(this.streamA)
    spy(this.StreamB)

  })

  afterEach(function(){
    spy.used.map(function(agent){
      spy.free.Push(spy.used.pop())
    })
  })

  it("must call transform with the data", function(){
    var ctx = this
    , dataA = new Buffer('some data')
    , dataB = 'some data'
    ;

    this.streamA.pipe(through2(function(chunk, enc, next){
      expect(ctx.streamA.spy).to.have.been.calledOnce.and.calledWidth(dataA)
    }))

    this.streamB.pipe(through2(function(chunk, enc, next){
      expect(ctx.streamB.spy).to.have.been.calledOnce.and.calledWidth(dataB)
    }))

    this.streamA.write(dataA)
    this.streamB.write(dataB)

  })

})

私のスパイ関数は_transformメソッドをラップし、私のスパイを呼び出して元の_transformを呼び出すことに注意してください

また、afterEach関数はスパイをリサイクルします。何百ものスパイを作成する可能性があるためです。

問題は、非同期コードをテストするときに難しくなります。次に、あなたの親友を約束します。上記のリンクには、いくつかのサンプルがあります。

3
markuz-gj

MemoryStream および sinon を使用して、ストリームをテストできます。これが、コードの一部をテストする方法です。

describe('some spec', function() {
    it('some test', function(done) {
        var outputStream = new MemoryStream();

        var spyCB = sinon.spy();

        outputStream.on('data', spyCB);

        doSomething(param, param2, outputStream, function() {
            sinon.assert.calledWith(spyCB, 'blah');

            done();
        });
    });
});
2
nacholibre

私が見つけた最良の方法は、イベントを使用することです

const byline = require('byline');
const fs = require('fs');

it('should process all lines in file', function(done){
   //arrange
   let lines = 0;
   //file with 1000 lines
   let reader = fs.readFileStream('./input.txt');
   let writer = fs.writeFileStream('./output.txt');
   //act
   reader.pipe(byline).pipe(writer);
   byline.on('line', function() {
     lines++;
   });
   //assert
   writer.on('close', function() {
     expect(lines).to.equal(1000);
     done();
   });
});

コールバックとしてdoneを渡すことで、mochaは呼び出されるまで待機してから次に進みます。

2
dmo

私はこれを使用したことがなく、古くなっていますが、 https://github.com/dominictarr/stream-spec が役立つ場合があります。

2
Martin Cleaver