TDD /使用NodeJS中的stream进行testing

我一直在试图find一个合理的方式来testing使用stream的代码。 有没有人find一个合理的方法/框架来帮助testing在nodejs中使用stream的代码?

例如:

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

我目前testing这种types的代码的方式要么涉及简化与代码stream,以便我可以抽象出一个未经testing的代码块或通过这样写:

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

有没有人想出更优雅的testingstream方式?

为此目的进行了streamtest。 它不仅使streamtesting更清晰,而且还允许testingV1和V2streamhttps://www.npmjs.com/package/streamtest

我也一直在使用memorystream ,但是之后把我的断言放到了finish事件中。 这样看起来更像是被testingstream的实际使用:

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

我感觉你很痛苦

我不知道任何框架来帮助testingstream,但如果看看这里 ,我正在开发一个stream库,你可以看到我如何解决这个问题。

这里是我在做什么的想法。

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

请注意,我的间谍function包装_transform方法,并调用我的间谍,并调用原来的_transform

此外, afterEach函数正在回收间谍,因为最终可能会创build数百个。

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

我没有使用过,这是相当古老的,但https://github.com/dominictarr/stream-spec可能会帮助。

您可以使用MemoryStream和sinon使用间谍来testingstream。 这是我如何testing我的一些代码。

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

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

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

通过传递完成,摩卡等待,直到它继续之前被调用。

将stream读入内存,并将其与预期的缓冲区进行比较。

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