使用 PHPUnit 测试输出到 php://stdout

Test output to php://stdout with PHPUnit

我正在使用 Symfony\Component\Console\Output\ConsoleOutput 写入控制台。

明确地说,我正在写信给 php://stdout

在我的单元测试中,我希望能够检查控制台的输出。

使用 PHPUnit 方法 expectOutputString(),我可以检查输出:

// Passes, as expected

public function testOutputBufferingEcho()
{
    $this->expectOutputString('Hello');

    echo 'Hello';
}

这也适用于 php://output 的输出:

// Passes, as expected

public function testOutputBufferingOutput()
{
    $this->expectOutputString('Hello');

    $out = fopen('php://output', 'w');
    fputs ($out, 'Hello');
    fclose($out);
}

但是,它不适用于 php://stdout 的输出(ConsoleOutput 作为默认值使用):

// Failed asserting that two strings are equal.
// --- Expected
// +++ Actual
// @@ @@
// -'Hello'
// +''

public function testOutputBufferingStdOut()
{
    $this->expectOutputString('Hello');

    $out = fopen('php://stdout', 'w');
    fputs ($out, 'Hello');
    fclose($out);
}

此外,似乎无法使用 ob_* 函数将输出直接捕获到 php://stdout

有没有用 PHPUnit 测试到 php://stdout 的输出?

或者是否有任何其他方法可以将 php://stdout 的输出捕获到字符串中(并在 PHPUnit 中进行测试)?

以上测试 运行 在 PHPUnit 5.5.5.

提前致谢。

有一种方法可以用任何其他资源替换 STDOUT:关闭它。下一个打开的资源将具有文件描述符“1”(STDOUT),因为这是第一个空闲资源。

fclose(STDOUT);
$fakestdout = fopen('php://memory', 'r+');

现在任何输出都进入 $fakestdout,您可以在测试用例中读取它。

唯一的问题是此操作无法还原。所以从现在开始,每次尝试写入 STDOUT(包括 "echo")都将转到 $fakestdout,或者在您关闭它之后无处可去。 STDOUT 一旦关闭就无法重新打开。

但是,如果您 运行 PHPUnit 使用 --stderr 参数将 STDERR 用于 PHPUnit 输出,这应该有效。

捕获 php://stdout(或任何其他流)的一种快速而肮脏的方法是使用更快且更脏的流过滤器。

示例:

class Intercept extends php_user_filter
{
    public static $cache = '';
    public function filter($in, $out, &$consumed, $closing)
    {
        while ($bucket = stream_bucket_make_writeable($in)) {
            self::$cache .= $bucket->data;
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }
}

stream_filter_register("intercept", "Intercept");

$stdout = fopen('php://stdout', 'w'); // or $yerSymfonyThingy->getStream()
stream_filter_append($stdout, "intercept");

fwrite($stdout, "Hello\n");
var_dump(Intercept::$cache);

输出:

Hello
string(6) "Hello
"

写入流的所有内容都会收集到 Intercept::$cache 中供您阅读。

如果愿意,您也可以通过将 PSFS_PASS_ON 替换为 PSFS_FEED_ME 来阻止流的正常输出。