21

I am using Symfony\Component\Console\Output\ConsoleOutput to write to the console.

Explicitly, I am writing to php://stdout.

In my unit tests, I would like to be able to check the output to the console.

Using the PHPUnit method expectOutputString(), I can check for output:

// Passes, as expected

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

    echo 'Hello';
}

This works with output to php://output too:

// Passes, as expected

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

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

However, it does not work with output to php://stdout (the one ConsoleOutput uses as default):

// 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);
}

Additionally, it appears it is not possible to use the ob_* functions to capture output directly to php://stdout.

Is there anyway to test output to php://stdout with PHPUnit?

Or is there any other way to capture the output to php://stdout into a string (and so test in PHPUnit)?

The above tests ran in PHPUnit 5.5.5.

Thank you in advance.

2 Answers 2

14

A quick and dirty way to capture php://stdout (or any other stream) would be to use a quicker and dirtier stream filter.

Example:

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);

Output:

Hello
string(6) "Hello
"

Everything written to the stream gets collected in Intercept::$cache for your perusal.

You can prevent normal output of the stream as well by replacing PSFS_PASS_ON with PSFS_FEED_ME if you like.

Sign up to request clarification or add additional context in comments.

1 Comment

I don't see why this would be "quick and dirty" - it's a perfectly fine solution :)
3

There is a way to replace STDOUT with any other resource: Close it. The next opened resource will have the file descriptor "1" (STDOUT) because this is the first free one.

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

Now any output goes to $fakestdout and you can read from it in your test case.

The only problem is that this operation cannot be reverted. So from now on every attempt to write to STDOUT (including "echo") will go to $fakestdout, or nowhere, after you close it. You cannot reopen STDOUT once closed.

But if you run PHPUnit with the --stderr argument to use STDERR for the PHPUnit output, this should work.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.