Pregunta TDD / prueba con transmisiones en NodeJS


He estado tratando de encontrar una forma razonable de probar el código que usa transmisiones. ¿Alguien ha encontrado una forma / marco razonable para ayudar a probar el código que usa flujos en nodejs?

Por ejemplo:

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

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

Mi forma actual de probar este tipo de código implica tanto simplificar el código con transmisiones que puedo resumirlo en un fragmento de código no probado o escribiendo algo como esto:

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

  });
});

¿Alguien ha encontrado formas más elegantes de probar transmisiones?


32
2018-04-17 18:55


origen


Respuestas:


Hecho streamtest para ese propósito. No solo hace que las pruebas de flujo sean más limpias, sino que también permite probar las transmisiones V1 y V2. https://www.npmjs.com/package/streamtest


10
2017-08-04 09:07



También he estado usando memorystream, pero luego poniendo mis afirmaciones en el finish evento. De esta forma, se parece más a un uso real de la secuencia que se prueba:

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 comprobación de los objetos se puede hacer así:

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

6
2018-06-25 09:30



Te siento dolor

No conozco ningún marco para ayudarme con las pruebas con transmisiones, pero si echas un vistazo aquí, donde estoy desarrollando una biblioteca de flujo, puede ver cómo abordo este problema.

Aquí hay una idea de lo que estoy haciendo.

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)

  })

})

Tenga en cuenta que mi función espía envuelve el _transform método y llame a mi espía y llame al original _transform 

También el afterEach la función es reciclar a los espías, porque puedes terminar creando cientos de ellos.

El problema se pone difícil cuando quieres probar el código asíncrono. Entonces promete a tu mejor amigo. El enlace que di más arriba tiene algo de muestra.


3
2018-05-17 22:30



Lee el flujo en la memoria y compáralo con el búfer esperado.

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

3
2018-05-09 00:44



No he usado esto, y es bastante viejo, pero https://github.com/dominictarr/stream-spec podría ayudar.


2
2017-09-03 10:48



Puedes probar las secuencias usando MemoryStream y Sinon mediante el uso de espías. Así es como probé algunos de mis códigos.

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
2018-05-18 13:51



La mejor forma que he encontrado es usar eventos

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

al pasar hecho como una devolución de llamada, mocha espera hasta que se llame antes de continuar.


1
2017-10-10 18:44