web-dev-qa-db-fra.com

TDD / test avec des flux dans NodeJS

J'ai essayé de trouver un moyen raisonnable de tester du code qui utilise des flux. Quelqu'un a-t-il trouvé un moyen/cadre raisonnable pour aider à tester le code qui utilise les flux dans nodejs?

Par exemple:

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

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

Ma façon actuelle de tester ce type de code consiste à simplifier tellement le code avec des flux que je peux l'abstraire sur un morceau de code non testé ou en écrivant quelque chose comme ceci:

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

  });
});

Quelqu'un a-t-il trouvé des moyens plus élégants de tester les flux?

35
Michael Wasser

Faites streamtest à cet effet. Il rend non seulement les tests de flux plus propres, mais permet également de tester les flux V1 et V2 https://www.npmjs.com/package/streamtest

14
nfroidure

J'ai également utilisé memorystream , mais j'ai ensuite placé mes assertions dans l'événement finish. De cette façon, cela ressemble plus à une utilisation réelle du flux testé:

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

La vérification des objets peut se faire comme ceci:

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

Lisez le flux en mémoire et comparez-le avec le tampon attendu.

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

Je te sens souffrir.

Je ne connais aucun cadre pour aider à tester avec les flux, mais si jetez un œil ici , où je développe une bibliothèque de flux, vous pouvez voir comment j'aborde ce problème.

voici une idée de ce que je fais.

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)

  })

})

Notez que ma fonction d'espionnage encapsule le _transform méthode et appeler mon espion et appeler l'original _transform

De plus, la fonction afterEach recycle les espions, car vous pouvez finir par en créer des centaines.

Le problème devient difficile lorsque vous souhaitez tester le code asynchrone. Promet ensuite votre meilleur ami. Le lien que j'ai donné ci-dessus en a un exemple.

3
markuz-gj

Vous pouvez tester les flux en utilisant MemoryStream et sinon en utilisant des espions. Voici comment j'ai testé une partie de mon code.

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

Le meilleur moyen que j'ai trouvé est d'utiliser les événements

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

en passant fait comme rappel, mocha attend qu'il soit appelé avant de continuer.

2
dmo

Je ne l'ai pas utilisé, et c'est assez ancien, mais https://github.com/dominictarr/stream-spec pourrait aider.

2
Martin Cleaver