【问题标题】:TDD/ testing with streams in NodeJS在 NodeJS 中使用流进行 TDD/测试
【发布时间】:2014-06-02 04:51:00
【问题描述】:

我一直在尝试寻找一种合理的方法来测试使用流的代码。有没有人找到一种合理的方法/框架来帮助测试在 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();
    });

  });
});

有没有人想出更优雅的测试流的方法?

【问题讨论】:

    标签: node.js testing stream tdd


    【解决方案1】:

    为此目的进行了流测试。它不仅使流测试更清洁,还允许测试 V1 和 V2 流https://www.npmjs.com/package/streamtest

    【讨论】:

    • 感谢您的库。这正是我所需要的,而且效果很好。
    • 你成就了我的一天!感谢这个@nfroidure
    【解决方案2】:

    我也一直在使用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();
          });
    .
    .
    .
    

    【讨论】:

      【解决方案3】:

      将 Stream 读入内存并与预期的 Buffer 进行比较。

      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】:

        我觉得你很痛苦。

        我不知道有什么框架可以帮助测试流,但是如果看看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)
        
          })
        
        })
        

        注意我的spy函数封装了_transform方法,调用我的spy,调用原来的_transform

        另外,afterEach 函数正在回收间谍,因为您最终可以创建数百个。

        当您想要测试异步代码时,问题变得很棘手。然后答应你最好的朋友。我上面给出的链接有一些示例。

        【讨论】:

          【解决方案5】:

          我没用过这个,而且很老了,但https://github.com/dominictarr/stream-spec 可能会有所帮助。

          【讨论】:

            【解决方案6】:

            您可以使用间谍来测试使用MemoryStreamsinon 的流。以下是我测试我的一些代码的方法。

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

            【讨论】:

              【解决方案7】:

              我发现最好的方法是使用事件

              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 会等到它被调用后再继续。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2015-10-10
                • 2020-02-28
                • 1970-01-01
                • 1970-01-01
                • 2019-01-08
                • 2020-05-12
                • 1970-01-01
                • 2018-02-25
                相关资源
                最近更新 更多