在Mocha测试期间将stdout和stderr静音

smi*_*lli 9 unit-testing stdout mocha.js stderr node.js

我将通过承认我可能正在做一些我不应该做的事来作为序言.但由于我已经深入了解,我不妨理解为什么会发生这种情况.

我正在使用Mocha来测试一些Node.js代码.此代码使用Winston日志记录库,它直接调用process.stdout.write()process.stderr.write()(源代码).效果很好; 我没有抱怨这种行为.

然而,当我对这段代码进行单元测试时,Mocha测试运行器的输出偶尔会散布着日志输出行,这在一些记者(dot,bdd)中是丑陋的,在其他记者中是彻头彻尾的无效(xunit).我想在不修改或子类化Winston的情况下阻止此输出,我想避免修改应用程序本身,如果我可以避免它.

我得到的是一组实用程序函数,可以使用no-op函数临时替换Node内置函数,反之亦然:

var stdout_write = process.stdout._write,
    stderr_write = process.stderr._write;

function mute() {
    process.stderr._write = process.stdout._write = function(chunk, encoding, callback) {
        callback();
    };
}

function unmute() {
    process.stdout._write = stdout_write;
    process.stderr._write = stderr_write;
}
Run Code Online (Sandbox Code Playgroud)

在各种测试规范中,我mute()在任何产生不需要的输出的调用或断言之前直接调用,然后unmute()直接调用.它感觉有点hacky,但它确实有效 - 运行测试时,控制台上没有出现单字节的不需要的输出.

现在变得奇怪了!

我第一次尝试将输出重定向到文件:

mocha spec_file.js > output.txt
Run Code Online (Sandbox Code Playgroud)

不想要的输出回来了!发送到stdout的每个输出都出现在文件中.添加2>&1,我也得到文件中的stderr.但是,无论如何,控制台上都没有出现任何内容.

为什么测试代码在两次调用之间表现如此不同?我的直觉是,Mocha正在做一些测试,以确定它是否写入TTY,但我无法发现一个明显的地方,它改变了它的写入行为.

还有一个更广泛的问题,是否有任何正确的方法可以在测试期间将stdout/stderr静音,而不会将所有潜在记录的应用程序代码包装在检查测试环境的条件中?

Qua*_*ong 5

请参阅https://www.npmjs.org/package/mute

it('should shut the heck up', function (done) {
    var unmute = mute()
    app.options.defaults = true;

    app.run(function() {
        unmute();

        helpers.assertFiles([
            ['package.json', /"name": "temp-directory"/],
            ['README.md',    /# TEMP.Directory/]
        ]);

        done();
    });
});
Run Code Online (Sandbox Code Playgroud)


smi*_*lli 3

我发现了这种行为的可能原因。它确实与 stdout/stderr 是否是 TTY 有关。

当脚本在控制台中运行时,它们都是 TTY,并且 和process.stdout似乎process.stderr是 的实例tty.WriteStream,而不是我最初假设的stream.Writable. 就我的交互而言,这两个类实际上并没有那么不同——两者都有write()调用内部_write()方法的公共方法,并且都共享相同的方法签名。

当通过管道传输到文件时,情况变得有些不同。process.stdout并且process.stderr是不是立即熟悉的不同类的实例。据我所知,这是一个fs. SyncWriteStream,但这是在黑暗中刺伤。不管怎样,这个类没有_write()方法,所以尝试重写它是没有意义的。

解决方案是提高一级并使用write()而不是进行静音_write()。它做同样的事情,并且无论输出去往何处,它都会一致地执行。