如何在Node工具中实现验证stdin.isTTY不同行为的测试?
我在Node中实现了一个CLI工具,它希望数据通过终端管道传输,或者作为命令行args传递:
cli.js
#!/usr/bin/env node
const { stdin, exit } = require('process');
const parseCliArgs = (argv) => {
// parse args...
return argv;
};
const readFromStdin = async () => {
stdin.setEncoding('utf-8');
return new Promise(((resolve, reject) => {
let data = '';
stdin.on('readable', () => {
let chunk;
while ((chunk = stdin.read()) !== null) {
data += chunk;
}
});
stdin.on('end', () => {
resolve(data);
});
stdin.on('error', err => reject(err));
}));
};
const main = async (argv) => {
let args;
console.info('isTTY: ', stdin.isTTY);
if (stdin.isTTY) {
console.info('Parse arguments');
args = parseCliArgs(argv);
} else {
console.info('Read from stdin');
args = await readFromStdin();
}
console.info(args);
};
main(process.argv)
.catch((err) => {
console.error(err);
exit(1);
});该工具在从终端使用时按预期工作,例如,将数据输送到CLI脚本:
$ echo "hello" | ./cli.js
isTTY: undefined
Read from stdin
hello并且将数据作为命令行参数传递也如预期的那样工作:
$ ./cli.js hello
isTTY: true
Parse arguments
[
'/usr/local/Cellar/node/13.2.0/bin/node',
'[my local path]/cli.js',
'hello'
]现在,我尝试编写一个测试来验证这些行为:
cli.spec.js
const { execSync } = require('child_process');
// pipe stdio and stdout from child process to node's stdio and stdout
const CHILD_PROCESS_OPTIONS = { stdio: 'inherit' };
describe('cli test', () => {
it('pipe data to CLI tool', () => {
const command = `echo "hello" | ${__dirname}/cli.js`;
execSync(command, CHILD_PROCESS_OPTIONS);
});
it('pass data as CLI args', () => {
const command = `${__dirname}/cli.js "hello"`;
execSync(command, CHILD_PROCESS_OPTIONS);
});
});pipe data to CLI tool测试按预期工作(并提供与从命令行执行时相同的输出)。
pass data as CLI args测试无限期挂起。看看输出,我发现
isTTY: undefined
Read from stdin基于这一观察,我得出结论,stdin.isTTY没有得到正确的处理(这导致readFromStdin()函数返回的承诺仍未得到解决,因此挂起了测试)。
pass data as CLI args通过?process.stdin.isTTY吗?发布于 2019-12-13 13:31:59
事实证明,这些测试是按预期工作的,但并不是在所有环境中都能工作。在从终端(例如npm test和朋友)执行时,测试按预期通过。但是,最初的实现没有通过,从那时起,我尝试使用IDE中的调试器来找到解决方案,但它仍然不起作用。如果在所有环境中都有另一个解决方案,请提交另一个答案。
发布于 2021-07-05 18:38:17
对于任何遇到这种情况的人来说,这都与Node.js如何生成子进程有关。具体来说,这是stdio选项 API支持的child_process。与此相关的问题在Node.js核心中是开放的。
当您在Node.js中生成子进程时,它在它和父进程之间有管道/流来处理stdio 默认情况下。因此,父程序可以调用child.stdin.write()将数据发送到子节点的stdin,还可以读取child.stderr和child.stdout流。
在使用同步child_process API时,stdin (AFAICT)非常令人困惑,因为您根本不能对子write()进行write()。您可以向每个文档传递一个Stream引用,但我无法使用tty.Read/WriteStream来计算它。
我遇到了这个问题,发现将options.stdio[0]设置为inherit解决了这个问题。这是因为我的父进程(测试)进程没有通过stdin传递任何内容,因此设置了TTY标志,子进程将继承它并按预期工作。我认为ignore可能有用,但没有用。
这里是我的完整测试用例,下面是一个例子。要测试Node.js CLI,其中您的CLI通过isTTY检测管道,但也支持文件args,请尝试如下:
try {
const stdout = execSync('your-cli.js some-file.txt', {
stdio: [
// Child will inherit process.stdin of
// this parent process. This has isTTY
// set to true, so the child will also
// be set to true and parse the "some-file.txt"
// arg instead of waiting on stdin
'inherit',
// Capture stdout from child. This is returned from
// the call, and attached to an exception if one is
// thrown, i.e error.stdout
'pipe',
// Capture stderr from child. This will be attached
// as a "stderr" property if an exception is raised
'pipe'
]
});
console.log('Child Process stdout was:', stdout)
} catch (e) {
console.log('Error in child. Child stderr was:', e.stderr)
}https://stackoverflow.com/questions/59270696
复制相似问题