Ответ 1
Сделано для этой цели. Это не только упрощает проверку потоков, но также позволяет тестировать потоки V1 и V2 https://www.npmjs.com/package/streamtest
Я пытаюсь найти разумный способ протестировать код, который использует потоки. Кто-нибудь нашел разумный способ/структуру, чтобы помочь тестировать код, который использует потоки в 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();
});
});
});
Кто-нибудь придумал более элегантные способы тестирования потоков?
Сделано для этой цели. Это не только упрощает проверку потоков, но также позволяет тестировать потоки V1 и V2 https://www.npmjs.com/package/streamtest
Я также использовал 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();
});
.
.
.
Я чувствую, что ты болен.
Я не знаю никаких фреймворков, чтобы помочь тестировать потоки, но если вы посмотрите здесь, где я разрабатываю библиотеку потоков, вы можете увидеть, как я подхожу к этой проблеме.
вот идея того, что я делаю.
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
и вызывает моего шпиона и вызывает исходный _transform
Кроме того, функция afterEach
перерабатывает шпионов, потому что вы можете создать сотни из них.
Проблема усложняется, когда вы хотите протестировать асинхронный код. Тогда promises ваш лучший друг. Ссылка, приведенная выше, содержит несколько примеров.
Я не использовал это, и он довольно старый, но https://github.com/dominictarr/stream-spec может помочь.
Вы можете протестировать потоки, используя MemoryStream и sinon с помощью шпионов. Вот как я проверил некоторые из моего кода.
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();
});
});
});
Прочитайте поток в памяти и сравните его с ожидаемым буфером.
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);
}
});
});
Лучший способ, который я нашел, - использовать события
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();
});
});
пройдя в качестве обратного вызова, мокко ждет, пока он не будет вызван, прежде чем двигаться дальше.