217

When running a PHPUnit test, I would like to be able to dump output so I can debug one or two things.

I have tried the following (similar to the PHPUnit Manual example);

class theTest extends PHPUnit_Framework_TestCase
{
    /**
     * @outputBuffering disabled
     */
    public function testOutput() {
        print_r("Hello World");
        print "Ping";
        echo "Pong";
        $out = "Foo";
        var_dump($out);
    }   
}

With the following result:

PHPUnit @package_version@ by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 0 assertions)

Notice there is none of the expected output.

I'm using the HEAD versions of the git repos as of September 19th, 2011.

Output of php -version:

$ php -version
PHP 5.2.9 (cli) (built: Dec  8 2010 11:36:37) 
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2009 Zend Technologies
    with Xdebug v2.1.0, Copyright (c) 2002-2010, by Derick Rethans

Is there anything I'm doing wrong, or is this potentially a PHPUnit bug?

kenorb
  • 155,785
  • 88
  • 678
  • 743
Jess Telford
  • 12,880
  • 8
  • 42
  • 51
  • 1
    Where is the code that calls the `testOutput()` method? – Derrick Tucker Sep 20 '11 at 23:52
  • You try really desperately (echo, print, print_r, var_dump - it's basically all "output"), normally I don't have a problem doing output from tests. You can check if output buffering is enabled: http://www.php.net/manual/en/function.ob-get-level.php - And the safest way to forcefully "test" is to throw an exception BTW. – hakre Sep 20 '11 at 23:55
  • 3
    @DerrickTucker PHPUnit does this by calling `phpunit /path/to/tests/theTest.php` (if the above class were in the file `theTest.php`). – Jess Telford Sep 20 '11 at 23:57
  • @hakre `ob_get_level()` returns `1`. However, this is contradicted by the following code: `while (ob_get_level() > 0) { ob_end_flush(); }` which errors with `ob_end_clean(): failed to delete buffer. No buffer to delete.`. Curiouser and curiouser. – Jess Telford Sep 21 '11 at 00:05
  • `ob_end_clean()`-error: always check file and line of the error, might not be your code. – hakre Sep 21 '11 at 00:07
  • @hakre Indeed, it gives me [phpunit.php:46](https://github.com/sebastianbergmann/phpunit/blob/master/phpunit.php#L46) which is not so useful other than saying it's the code somewhere inside the test (which is where `ob_end_flush` is being called), correct? – Jess Telford Sep 21 '11 at 00:16
  • 2
    It's saying that it's phpunit's code that is triggering the error - obviously because phpunits output swallowing is active (but you broke it). Look precisely, the function name differs as well. – hakre Sep 21 '11 at 00:20
  • I have to come back to the issue as I was sure that this works. I have this working not setting any special annotation by calling a private test method that uses `printf`. The output get's written to STDOUT when running the testrunner from shell. I do nothing special. PHPUnit 3.5.11. – hakre Oct 02 '11 at 21:43
  • I already gave an answer that works well below, but really for anyone seeking this, please consider: XDEBUG IS THE CORRECT ANSWER!!! Get debugging working and you won't have to deal with all this, plus XDebug works better than printing to the console in so many other ways. Invest the time and get it working, you'll thank me. The hacks in these answers should only be for weird edge-case emergencies. – jerclarke Dec 10 '21 at 21:43

21 Answers21

259

UPDATE

Just realized another way to do this that works much better than the --verbose command line option:

class TestSomething extends PHPUnit_Framework_TestCase {
    function testSomething() {
        $myDebugVar = array(1, 2, 3);
        fwrite(STDERR, print_r($myDebugVar, TRUE));
    }
}

This lets you dump anything to your console at any time without all the unwanted output that comes along with the --verbose CLI option.


As other answers have noted, it's best to test output using the built-in methods like:

$this->expectOutputString('foo');

However, sometimes it's helpful to be naughty and see one-off/temporary debugging output from within your test cases. There is no need for the var_dump hack/workaround, though. This can easily be accomplished by setting the --verbose command line option when running your test suite. For example:

$ phpunit --verbose -c phpunit.xml

This will display output from inside your test methods when running in the CLI environment.

See: Writing Tests for PHPUnit - Testing Output.

kenorb
  • 155,785
  • 88
  • 678
  • 743
  • 7
    sorry, missed we write to stderr. Indeed works. I just was forced to use `file_put_contents('php://stderr', $myDebugVar, FILE_APPEND);` instead, because I had message `Use of undefined constant STDERR - assumed 'STDERR'` with **fwrite**. – Serge Nov 19 '13 at 08:07
  • The problem is this does not seem to work with process isolation. – donquixote Jun 11 '14 at 15:26
  • @donquixote Not surprising as the test will execute in another process whose STDERR stream output is likely discarded ... –  Jun 11 '14 at 16:51
  • 2
    You can aslo use `STDOUT` instead of `STERR` – Chris Feb 28 '15 at 20:06
  • @Chris Are you sure about this? You may be right but I haven't tested it. I *think* `STDOUT` is intercepted by phpunit via `ob_start()` (which is why you can't just `echo` things in the first place). That's the logic behind using `STDERR` –  Feb 28 '15 at 22:38
  • 3
    Yes. It works and seems to output the same way as `STDERR`. I am using `PHPUnit 4.5.0` in windows cmd line. an `echo` statement does not give the same results. `echo` does output but only after the test result is displayed. `fwrite(STDERR, 'string')` or `fwrite(STDOUT,'string')` produce the same results: An output before the test result is displayed. – Chris Feb 28 '15 at 23:42
  • have a look here: https://phpunit.de/manual/current/en/fixtures.html at example 4.2. they are using STDOUT – Chris Mar 01 '15 at 17:53
  • Thank you! This was the only solution that worked for me with phpunit 7.4.3 (incl. ``--debug`` and ``--verbose``) on a Docker container. Everything else was swallowed - even log writes! – Juha Untinen Apr 10 '19 at 08:24
43

Try using --debug

Useful if you're trying to get the right path to an include or source data file.

d-_-b
  • 21,536
  • 40
  • 150
  • 256
chim
  • 8,407
  • 3
  • 52
  • 60
  • 3
    This is the correct answer for me. All the fwrite statements written in the earlier answers didn't work for me. – Kim Stacks Dec 28 '14 at 06:27
38

Update: See rdlowrey's update below regarding the use of fwrite(STDERR, print_r($myDebugVar, TRUE)); as a much simpler work around


This behaviour is intentional (as jasonbar has pointed out). The conflicting state of the manual has been reported to PHPUnit.

A work-around is to have PHPUnit assert the expected output is empty (when infact there is output) which will trigger the unexpected output to be shown.

class theTest extends PHPUnit_Framework_TestCase
{
    /**
     * @outputBuffering disabled
     */
    public function testOutput() {
        $this->expectOutputString(''); // tell PHPUnit to expect '' as output
        print_r("Hello World");
        print "Ping";
        echo "Pong";
        $out = "Foo";
        var_dump($out);
    }   
}

gives:

PHPUnit @package_version@ by Sebastian Bergmann.

F

Time: 1 second, Memory: 3.50Mb

There was 1 failure:

1) theTest::testOutput
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-''
+'Hello WorldPingPongstring(4) "Foo"
+'

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Be certain to disable any other assertions you have for the test as they may fail before the output assertion is tested (and hence you wont see the output).

Community
  • 1
  • 1
Jess Telford
  • 12,880
  • 8
  • 42
  • 51
13

Just call ob_flush() after outputting text

Example code:

    public function testDebugOutputToCli() {
        var_dump(new DateTime());
        ob_flush();
    }

Screenshot of code and output:

Screenshot of using ob_flush to echo out content that would otherwise have been hidden by PHPUnit

Why? PHPUnit is always output buffering, so we need to dump the buffer when debugging

I was struggling with all the answers above, especially because the selected answer –using codecept_debug() with --debug as the manual says– caused a huge wave of debug output that made it impossible to use for me.

I was reading the PHPUnit manual like a good nerd and stumbled onto this, which I think explains what causes this whole issue across all of PHPUnit, not just Codeception:

PHPUnit manual, Testing Output: “Sometimes you want to assert that the execution of a method, for instance, generates an expected output (via echo or print, for example). The PHPUnit\Framework\TestCase class uses PHP’s Output Buffering feature to provide the functionality that is necessary for this.”

This makes total sense and explains why we don't see the output. PHPUnit is saving it up in case we want to examine the comments! This is how it should always work in our actual tests, we of course don't want random stuff getting to the screen just because we called a function that uses echo.

But when we're debugging, we just want to see the text right away, and understanding all this, the solution is clear: Just use ob_flush() to print the contents of the output buffer on demand!

Three cheers for reading the fun manuals!

P.S. Also found this tip hidden in How to show var_dumps in phpunit or codeception by Julian on dev.to

jerclarke
  • 1,219
  • 1
  • 13
  • 23
  • FWIW I'll just add: The real correct answer is to get XDebug set up to work with PHPUnit, then debug with that!!! I figured out the solution above while stuck on getting XDebug working, but once I got debugging setup, I am happy to never use the output-to-CLI hack every again! – jerclarke Dec 10 '21 at 21:42
10

It's not a bug, but very much intentional. Your best bet is to write to a log file of some kind and tail the log to watch for output.

If you are trying to TEST output, check this out.

Also:

Note: Please note that PHPUnit swallows all output that is emitted during the execution of a test. In strict mode, a test that emits output will fail.

kenorb
  • 155,785
  • 88
  • 678
  • 743
jasonbar
  • 13,333
  • 4
  • 38
  • 46
  • 1
    If it were intentional, then surely the manual would not [give an example of it](http://www.phpunit.de/manual/current/en/fixtures.html#fixtures.examples.TemplateMethodsTest.php)? Also, not trying to test the output itself. Just using it to eyeball some results causing tests to fail when they shouldn't. – Jess Telford Sep 21 '11 at 00:09
  • As written: I normally don't have a problem to echo out when tests run. You might have some configuration that's catching input. – hakre Sep 21 '11 at 00:13
  • 1
    If it weren't intentional, then surely the manual would not [say that it was](http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.output.tables.api). – jasonbar Sep 21 '11 at 00:14
  • 1
    So it seems a conflict in the documentation. @hakre appears to be under the same impression I was (that it shouldn't be swallowed) - which part of the documentation is correct? – Jess Telford Sep 21 '11 at 00:19
  • Output generating tests ONLY fail when --disallow-test-output (or conf file has beStrictAboutOutputDuringTests="true") - documentation now says "A test that emits output, for instance by invoking print in either the test code or the tested code, will be marked as risky when this check is enabled." https://phpunit.readthedocs.io/en/8.4/risky-tests.html#risky-tests – NULL pointer Nov 12 '19 at 01:25
7

I'm having some luck with VisualPHPUnit, and it does helpfully show output, among other things.

class TestHello extends PHPUnit_Framework_TestCase 
{
    public function test_Hello() 
    {
        print "hello world";
    }
}

TestHello results

Bob Stein
  • 16,271
  • 10
  • 88
  • 101
  • 1
    Hmm, why the downvote? How is this not helpful as an alternative way to dump debug output in a PHPUnit test? – Bob Stein Feb 04 '14 at 02:13
  • 1
    I'm *guessing* this is downvoted because if anyone tries running this you'll get a syntax error. A massive one. – Jimbo Apr 15 '14 at 08:21
  • D'oh I forgot the function. Now it's fixed, tested, cut and pasted. Thanks, @Jimbo – Bob Stein Apr 15 '14 at 15:37
  • Sadly it's not compatible with PHP 7 at the moment, apparently: "VisualPHPUnit is not php 7 compatible at this time due to the way phpunit is utilized. Php 7 will be supported in the next major release" – leo Jan 24 '20 at 06:37
7

You should really think about your intentions: If you need the information now when debugging to fix the test, you will need it next week again when the tests break.

This means that you will need the information always when the test fails - and adding a var_dump to find the cause is just too much work. Rather put the data into your assertions.

If your code is too complex for that, split it up until you reach a level where one assertion (with a custom message) tells you enough to know where it broke, why and how to fix the code.

cweiske
  • 30,033
  • 14
  • 133
  • 194
  • 1
    I agree 100% with everything you said. I'm using PHPUnit to do Integration tests that ultimately call one of Google's XML APIs. All Unit tests passed (with API calls mocked), but the final test (with live API calls) failed. Turned out it was the [Google API's fault](https://groups.google.com/group/google-content-api-for-shopping/browse_thread/thread/1ac7c937cd62af2c) but in the mean-time, I wanted to dump the raw HTTP response. – Jess Telford Sep 22 '11 at 00:22
  • 2
    What if you need to debug your code on the way to achieving what you've outlined here? – David Meister May 24 '15 at 16:03
  • 4
    This is why I don't like answers that second-guess what the users want to do. I'm here because I have test that wait for cache clears. With 5 second cache ttls, that means my test *appears to hang* for ~ 16 seconds. I would just like to emit some notice to the user that no, nothing's wrong, we're just waiting for caches to time out. If people can just answer the question, then people with other use cases would have their answer, too. – user151841 Oct 14 '19 at 20:49
6

Just use the --verbose flag when execute phpunit.

$ phpunit --verbose -c phpunit.xml 

The advantage of this method is that you don't need to change the test code, you can print strings, var_dump's o anything you wish always and it will be shown in the console only when verbose mode is set.

I hope this helps.

Fabricio
  • 3,248
  • 2
  • 16
  • 22
5

PHPUnit is hiding the output with ob_start(). We can disable it temporarily.

    public function log($something = null)
    {
        ob_end_clean();
        var_dump($something);
        ob_start();
    }
Slawa
  • 1,141
  • 15
  • 21
4

This was taken from PHPUnit Docs about Fixtures.

This should allow you to dump information at any point durring the phpunit test life cycle.

Just replace __METHOD__ in the code below with whatever you want to output

Example 4.2: Example showing all template methods available

<?php
class TemplateMethodsTest extends PHPUnit_Framework_TestCase
{
    public static function setUpBeforeClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function setUp()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function assertPreConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public function testOne()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(TRUE);
    }

    public function testTwo()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(FALSE);
    }

    protected function assertPostConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function tearDown()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public static function tearDownAfterClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function onNotSuccessfulTest(Exception $e)
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        throw $e;
    }
}
?>
kenorb
  • 155,785
  • 88
  • 678
  • 743
Chris
  • 2,166
  • 1
  • 24
  • 37
4

In laravel 5 you can use dump(), Dump the content from the last response.

class ExampleTest extends TestCase{
    public function test1()
    {
        $this->post('/user', ['name' => 'Gema']);
        $this->dump();
    }
}

gives

Branny Bk
  • 41
  • 1
3

For some cases one could use something like that to output something to the console

class yourTests extends PHPUnit_Framework_TestCase
{
    /* Add Warnings */
    protected function addWarning($msg, Exception $previous = null)
    {
        $add_warning = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_Warning($msg, 0, $previous);
        $add_warning->addWarning($this, $msg, time());
        $this->setTestResultObject($add_warning);
    }

    /* Add errors */
    protected function addError($msg, Exception $previous = null)
    {
        $add_error = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_AssertionFailedError($msg, 0, $previous);
        $add_error->addError($this, $msg, time());
        $this->setTestResultObject($add_error);
    }

    /* Add failures */
    protected function addFailure($msg, Exception $previous = null)
    {
        $add_failure = $this->getTestResultObject();
        $msg = new PHPUnit_Framework_AssertionFailedError($msg, 0, $previous);
        $add_failure->addFailure($this, $msg, time());
        $this->setTestResultObject($add_failure);
    }

    public function test_messages()
    {
        $this->addWarning("Your warning message!");
        $this->addError("Your error message!");
        $this->addFailure("Your Failure message");
    }

    /* Or just mark test states! */
    public function test_testMarking()
    {
        $this->markTestIncomplete();
        $this->markTestSkipped();
    }
}
mkungla
  • 3,390
  • 1
  • 26
  • 37
3

Hackish, but works: Throw an exception with the debug output as its message.

class theTest extends PHPUnit_Framework_TestCase
{
    public function testOutput() {
        throw new \Exception("hello");
    }   
}

Yields:

...
There was 1 error:

1) theTest::testOutput
Exception: hello
1

I output my Testresults HTML based, in this case it was helpfull to flush the content:

var_dump($array);
ob_flush();

There is a second PHP Method

flush() 

which i not has tried.

Sudo
  • 95
  • 1
  • 8
1

In short, phpunit supresses STDOUT. It writes to STDERR by default, unless you add --verbose or --debug . You can do one of those things:

  • print your debug output to STDERR instead
  • var_dump your debug as usual but add --verbose to the phpunit command line
  • var_dump your debug as usual but add a line ob_flush(); beneath it
  • use the correct commands in phpunit for testing exactly what you're trying to test here

Obviously, the last thing is the Good Thing to do, and the rest are quick temporary hacks.

commonpike
  • 10,499
  • 4
  • 65
  • 58
1

It is possible to use Symfony\Component\Console\Output\TrimmedBufferOutput and then test the buffered output string like this:

use Symfony\Component\Console\Output\TrimmedBufferOutput;

//...
public function testSomething()
{
   $output = new TrimmedBufferOutput(999);
   $output->writeln('Do something in your code with the output class...');
   
   //test the output:
   $this->assertStringContainsString('expected string...', $output->fetch());   
}
gpupo
  • 942
  • 9
  • 16
0

I had to modify source code for this code to work so you need to add URL for this forked repos to composer for this will work

class TestCase extends \PHPUnit_Framework_TestCase
{
    /**
     *  Save last response
     * @var Response|null A Response instance
     */
    static $lastResponse;
    /**
     *  Modify to save response
     *
     * @param  string $method
     * @param  string $uri
     * @param  array $parameters
     * @param  array $files
     * @param  array $server
     * @param  string $content
     * @param  bool $changeHistory
     * @return \Illuminate\Http\Response
     */
    final public function call(
        $method,
        $uri,
        $parameters = [],
        $files = [],
        $server = [],
        $content = null,
        $changeHistory = true
    ) {

        $response = parent::call($method, $uri, $parameters, $files, $server, $content, $changeHistory);
        static::$lastResponse = $this->client->getResponse();
        return $response;
    }


    /**
     * Modify message to add response text
     *
     * @param mixed $value
     * @param PHPUnit_Framework_Constraint $constraint
     * @param string $message
     * @since  Method available since Release 3.0.0
     */
    final public static function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '')
    {
        $message .= PHP_EOL . static::$lastResponse . PHP_EOL;
        parent::assertThat($value, $constraint, $message);
    }
}
Gadelkareem
  • 1,067
  • 13
  • 30
0

Here are few methods useful for printing debug messages in PHPUnit 4.x:

  • syslog(LOG_DEBUG, "Debug: Message 1!");

    More practical example:

    syslog(LOG_DEBUG, sprintf("%s: Value: %s", __METHOD__, var_export($_GET, TRUE)));
    

    Calling syslog() will generate a system log message (see: man syslog.conf).

    Note: Possible levels: LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING, LOG_ERR, etc.

    On macOS, to stream the syslog messages in realtime, run:

    log stream --level debug --predicate 'processImagePath contains "php"'
    
  • fwrite(STDERR, "LOG: Message 2!\n");

    Note: The STDERR constant is not available if reading the PHP script from stdin. Here is the workaround.

    Note: Instead of STDERR, you can also specify a filename.

  • file_put_contents('php://stderr', "LOG: Message 3!\n", FILE_APPEND);

    Note: Use this method, if you don't have STDERR constant defined.

  • register_shutdown_function('file_put_contents', 'php://stderr', "LOG: Message 4!\n", FILE_APPEND);

    Note: Use this method, if you'd like to print something at the very end without affecting the tests.

To dump the variable, use var_export(), e.g. "Value: " . var_export($some_var, TRUE) . "\n".

To print above messages only during verbose or debug mode, see: Is there a way to tell if --debug or --verbose was passed to PHPUnit in a test?


Although if testing the output is part of the test it-self, check out: Testing Output docs page.

kenorb
  • 155,785
  • 88
  • 678
  • 743
0

it's a paid product, but I find it does the job well: Ray from Spatie https://spatie.be/products/ray

just use it like this:

ray('message')

and the message will show up in Ray output window

iteratorX
  • 9
  • 1
-1

If you use Laravel, then you can use logging functions such as info() to log to the Laravel log file under storage/logs. So it won't appear in your terminal but in the log file.

leo
  • 1,175
  • 12
  • 13
-1

You can use PHPunit default way of showing messages to debug your variables inside your test like this:

$this->assertTrue(false,$your_variable);
  • This will fail test execution. What if I want to output content, but not interrupt tests execution? – Justinas May 13 '21 at 08:11