如何在 NodeJS 中模拟流
How to mock streams in NodeJS
我正在尝试对我的一个 node-js 模块进行单元测试,该模块大量处理流。我正在尝试模拟一个流(我将写入),因为在我的模块中我有我想触发的“.on('data/end)”监听器。本质上我希望能够做这样的事情:
var mockedStream = new require('stream').readable();
mockedStream.on('data', function withData('data') {
console.dir(data);
});
mockedStream.on('end', function() {
console.dir('goodbye');
});
mockedStream.push('hello world');
mockedStream.close();
这会执行,但在我执行推送后 'on' 事件永远不会被触发(并且 .close() 无效)。
我能找到的关于流的所有指南都使用 'fs' 或 'net' 库作为创建新流 (https://github.com/substack/stream-handbook) 的基础,或者他们用 sinon 模拟它但是嘲笑很快就变得很长。
是否有提供像这样的虚拟流的好方法?
我不应该使用 Push,而应该使用“.emit(, );”
我的模拟代码现在可以工作了,看起来像:
var mockedStream = new require('stream').Readable();
mockedStream._read = function(size) { /* do nothing */ };
myModule.functionIWantToTest(mockedStream); // has .on() listeners in it
mockedStream.emit('data', 'Hello data!');
mockedStream.emit('end');
采纳答案仅部分正确。如果您只需要触发事件,那么使用 .emit('data', datum)
是可以的,但是如果您需要将此模拟流通过管道传输到其他任何地方,它就无法工作。
模拟可读流非常简单,只需要可读库。
let eventCount = 0;
const mockEventStream = new Readable({
objectMode: true,
read: function (size) {
if (eventCount < 10) {
eventCount = eventCount + 1;
return this.push({message: `event${eventCount}`})
} else {
return this.push(null);
}
}
});
现在您可以在任何地方传输此流,'data' 和 'end' 将触发。
节点文档中的另一个示例:
https://nodejs.org/api/stream.html#stream_an_example_counting_stream
有一个更简单的方法:stream.PassThrough
我刚刚发现 Node 很容易被忽略 stream.PassThrough
class,我相信这正是您要找的。
来自Node docs:
The stream.PassThrough class is a trivial implementation of a Transform stream that simply passes the input bytes across to the output. Its purpose is primarily for examples and testing...
问题代码,修改:
const { PassThrough } = require('stream');
const mockedStream = new PassThrough(); // <----
mockedStream.on('data', (d) => {
console.dir(d);
});
mockedStream.on('end', function() {
console.dir('goodbye');
});
mockedStream.emit('data', 'hello world');
mockedStream.end(); // <-- end. not close.
mockedStream.destroy();
mockedStream.push()
也可以,但作为 Buffer
所以你可能想要这样做:console.dir(d.toString());
基于@flacnut 的回答,我(在 NodeJS 12+ 中)使用 Readable.from()
构建预加载数据(文件名列表)的流:
const mockStream = require('stream').Readable.from([
'file1.txt',
'file2.txt',
'file3.txt',
])
在我的例子中,我想模拟 fast-glob.stream:
返回的文件名流
const glob = require('fast-glob')
// inject the mock stream into glob module
glob.stream = jest.fn().mockReturnValue(mockStream)
正在测试的函数中:
const stream = glob.stream(globFilespec)
for await (const filename of stream) {
// filename = file1.txt, then file2.txt, then file3.txt
}
很有魅力!
我正在尝试对我的一个 node-js 模块进行单元测试,该模块大量处理流。我正在尝试模拟一个流(我将写入),因为在我的模块中我有我想触发的“.on('data/end)”监听器。本质上我希望能够做这样的事情:
var mockedStream = new require('stream').readable();
mockedStream.on('data', function withData('data') {
console.dir(data);
});
mockedStream.on('end', function() {
console.dir('goodbye');
});
mockedStream.push('hello world');
mockedStream.close();
这会执行,但在我执行推送后 'on' 事件永远不会被触发(并且 .close() 无效)。
我能找到的关于流的所有指南都使用 'fs' 或 'net' 库作为创建新流 (https://github.com/substack/stream-handbook) 的基础,或者他们用 sinon 模拟它但是嘲笑很快就变得很长。
是否有提供像这样的虚拟流的好方法?
我不应该使用 Push,而应该使用“.emit(
我的模拟代码现在可以工作了,看起来像:
var mockedStream = new require('stream').Readable();
mockedStream._read = function(size) { /* do nothing */ };
myModule.functionIWantToTest(mockedStream); // has .on() listeners in it
mockedStream.emit('data', 'Hello data!');
mockedStream.emit('end');
采纳答案仅部分正确。如果您只需要触发事件,那么使用 .emit('data', datum)
是可以的,但是如果您需要将此模拟流通过管道传输到其他任何地方,它就无法工作。
模拟可读流非常简单,只需要可读库。
let eventCount = 0;
const mockEventStream = new Readable({
objectMode: true,
read: function (size) {
if (eventCount < 10) {
eventCount = eventCount + 1;
return this.push({message: `event${eventCount}`})
} else {
return this.push(null);
}
}
});
现在您可以在任何地方传输此流,'data' 和 'end' 将触发。
节点文档中的另一个示例: https://nodejs.org/api/stream.html#stream_an_example_counting_stream
有一个更简单的方法:stream.PassThrough
我刚刚发现 Node 很容易被忽略 stream.PassThrough
class,我相信这正是您要找的。
来自Node docs:
The stream.PassThrough class is a trivial implementation of a Transform stream that simply passes the input bytes across to the output. Its purpose is primarily for examples and testing...
问题代码,修改:
const { PassThrough } = require('stream');
const mockedStream = new PassThrough(); // <----
mockedStream.on('data', (d) => {
console.dir(d);
});
mockedStream.on('end', function() {
console.dir('goodbye');
});
mockedStream.emit('data', 'hello world');
mockedStream.end(); // <-- end. not close.
mockedStream.destroy();
mockedStream.push()
也可以,但作为 Buffer
所以你可能想要这样做:console.dir(d.toString());
基于@flacnut 的回答,我(在 NodeJS 12+ 中)使用 Readable.from()
构建预加载数据(文件名列表)的流:
const mockStream = require('stream').Readable.from([
'file1.txt',
'file2.txt',
'file3.txt',
])
在我的例子中,我想模拟 fast-glob.stream:
返回的文件名流 const glob = require('fast-glob')
// inject the mock stream into glob module
glob.stream = jest.fn().mockReturnValue(mockStream)
正在测试的函数中:
const stream = glob.stream(globFilespec)
for await (const filename of stream) {
// filename = file1.txt, then file2.txt, then file3.txt
}
很有魅力!