2014-04-17 75 views
30

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

    }); 
}); 

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

回答

3

我觉得你很痛苦。

我不知道任何框架,以帮助检测与数据流,但如果看看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功能是回收的间谍,因为你可以最终创造数百人。

问题变得很难,当你想测试异步代码。然后承诺你最好的朋友。上面给出的链接有一些样例。

2

您可以通过使用间谍来使用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(); 
     }); 
    }); 
}); 
6

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

最好的办法是通过传递完成的回调使用事件

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

,摩卡等待,直到它被称为继续前进。

1

将流读入内存并将其与期望的缓冲区进行比较。

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