如何在执行PHP单元测试期间在CLI中输出?

时间:2011-09-20 23:49:11

标签: php unit-testing phpunit

运行PHPUnit测试时,我希望能够转储输出,这样我就可以调试一两件事。

我尝试了以下内容(类似于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);
    }   
}

具有以下结果:

PHPUnit @package_version@ by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.00Mb

OK (1 test, 0 assertions)

请注意,没有预期的输出。

截至2011年9月19日,我正在使用git repos的HEAD版本。

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

我有什么问题,或者这可能是PHPUnit的错误吗?

21 个答案:

答案 0 :(得分:152)

<强>更新

刚刚意识到另一种方法可以比--verbose命令行选项更好地工作:

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

这使您可以随时将任何内容转储到控制台,而不会出现--verbose CLI选项随附的所有不需要的输出。


正如其他答案所述,最好使用内置方法测试输出,如:

$this->expectOutputString('foo');

但是,有时候调侃并在测试用例中看到一次性/临时调试输出是有帮助的。但是,不需要var_dump黑客/解决方法。通过在运行测试套件时设置--verbose命令行选项,可以轻松完成此操作。例如:

$ phpunit --verbose -c phpunit.xml

在CLI环境中运行时,这将显示测试方法内部的输出。

请参阅:Writing Tests for PHPUnit - Testing Output

答案 1 :(得分:32)

更新:有关使用fwrite(STDERR, print_r($myDebugVar, TRUE));作为更简单的解决方法,请参阅rdlowrey's update below


此行为是故意的(因为jasonbarpointed out)。手册的冲突状态为PHPUnit reported

解决方法是让PHPUnit断言预期的输出为空(当存在输出时),这将触发显示的意外输出。

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

给出:

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.

确保禁用测试中的任何其他断言,因为它们在测试输出断言之前可能会失败(因此您不会看到输出)。

答案 2 :(得分:27)

尝试使用--debug

如果您正在尝试获取包含或源数据文件的正确路径,则非常有用。

答案 3 :(得分:9)

这不是一个错误,而是非常有意的。最好的办法是写入某种日志文件并拖尾日志以观察输出。

如果您尝试测试输出,请检查this输出。

此外:

  

注意:请注意,PHPUnit会吞下所有发出的输出   在执行测试期间。在严格模式下,发出的测试   输出将失败。

答案 4 :(得分:7)

我对VisualPHPUnit感到很幸运,除了其他方面,它确实有助于显示输出。

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

TestHello results

答案 5 :(得分:5)

你应该考虑一下你的意图:如果你现在在调试时需要这些信息来修复测试,那么当测试中断时你将在下周再次需要它。

这意味着当测试失败时,您将需要始终的信息 - 并且添加var_dump以找到原因是太多的工作。而是将数据放入您的断言中。

如果您的代码过于复杂,请将其拆分,直至达到一个级别,其中一个断言(带有自定义消息)告诉您足以知道它在哪里损坏,为什么以及如何修复代码。

答案 6 :(得分:3)

在某些情况下,人们可以使用类似的东西向控制台输出内容

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

答案 7 :(得分:3)

在laravel 5中,您可以使用dump(),转储上一个响应中的内容。

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

gives

答案 8 :(得分:3)

Hackish,但有效:将调试输出作为消息抛出异常。

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

收率:

...
There was 1 error:

1) theTest::testOutput
Exception: hello

答案 9 :(得分:2)

这取自PHPUnit Docs about Fixtures

这应该允许您在phpunit测试生命周期的任何时候转储信息。

只需将下面代码中的__METHOD__替换为您要输出的内容

即可

示例4.2:显示所有可用模板方法的示例

<?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;
    }
}
?>

答案 10 :(得分:2)

执行 phpunit 时,只需使用 - verbose 标志。

$ phpunit --verbose -c phpunit.xml 

这种方法的优点是你不需要更改测试代码,你可以打印字符串,var_dump&#39;所以你想要的任何东西,它只会在时显示在控制台中设置了详细模式。

我希望这会有所帮助。

答案 11 :(得分:2)

PHPUnit用ob_start()隐藏了输出。我们可以暂时禁用它。

    public function log($something = null)
    {
        ob_end_clean();
        var_dump($something);
        ob_start();
    }

答案 12 :(得分:1)

我输出我的Testresults HTML,在这种情况下,刷新内容是有帮助的:

var_dump($array);
ob_flush();

还有第二个PHP方法

flush() 

我没试过。

答案 13 :(得分:1)

这是一款付费产品,但我发现它的效果很好:来自 Spatie 的 Ray https://spatie.be/products/ray

就这样使用它:

<块引用>

ray('message')

并且消息将显示在 Ray 输出窗口中

答案 14 :(得分:1)

您可以使用PHPunit默认的显示消息的方式来在测试中调试变量,如下所示:

$this->assertTrue(false,$your_variable);

答案 15 :(得分:0)

简而言之,phpunit禁止STDOUT。默认情况下,它将写入STDERR,除非您添加--verbose--debug。您可以执行以下操作之一:

  • 改为将调试输出打印到STDERR
  • var_dump像往常一样进行调试,但是将--verbose添加到phpunit命令行中
  • var_dump像往常一样进行调试,但在其下面添加一行ob_flush();
  • 在phpunit中使用正确的命令来准确测试您要在此处测试的内容

很明显,最后一件事情就是要做的事,其余的都是快速的临时黑客。

答案 16 :(得分:0)

以下是几种在PHPUnit 4.x中打印调试消息的方法:

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

    更实际的例子:

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

    调用syslog()将生成系统日志消息(请参阅:man syslog.conf)。

    注意:可能的级别:LOG_DEBUGLOG_INFOLOG_NOTICELOG_WARNINGLOG_ERR

    在macOS上,要实时流式传输系统日志消息,请运行:

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

    注意:如果从 stdin 读取PHP脚本,则STDERR常量不可用。这是workaround

    注意:您也可以指定文件名而不是STDERR

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

    注意:如果您没有定义STDERR constant,请使用此方法。

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

    注意:如果您想在最后打印一些内容而不影响测试,请使用此方法。

要转储变量,请使用var_export(),例如"Value: " . var_export($some_var, TRUE) . "\n"

要仅在详细或调试模式下打印上述消息,请参阅:Is there a way to tell if --debug or --verbose was passed to PHPUnit in a test?

虽然测试输出是测试的一部分,但请查看:Testing Output docs页面。

答案 17 :(得分:0)

可以使用Symfony\Component\Console\Output\TrimmedBufferOutput然后测试缓冲的输出字符串,如下所示:

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

答案 18 :(得分:0)

输出文本后调用ob_flush()

示例代码:

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

代码和输出截图:

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

为什么? PHPUnit一直是输出缓冲的,所以调试的时候需要转储缓冲

我一直在为上面的所有答案而苦苦挣扎,尤其是因为所选的答案——使用 codecept_debug()--debug,如手册所述——导致了大量调试输出,使我无法使用.

我像个书呆子一样阅读 PHPUnit 手册并偶然发现了这一点,我认为这解释了导致整个 PHPUnit 整个问题的原因,而不仅仅是 Codeception:

<块引用>

PHPUnit manual, Testing Output:“有时你想断言一个方法的执行,例如,产生一个预期的输出(例如,通过 echo 或 print)。 PHPUnit\Framework\TestCase 类使用 PHP 的输出缓冲功能来提供所需的功能。”

这完全有道理并解释了为什么我们看不到输出。 PHPUnit 正在保存它,以防我们要检查注释!这就是它在我们的实际测试中应该始终工作的方式,我们当然不希望随机的东西仅仅因为我们调用了一个使用 echo 的函数而出现在屏幕上。

但是在调试的时候,我们只想马上看到文字,明白了这一切,解决办法很明确:只要使用ob_flush()按需打印输出缓冲区的内容!

为阅读有趣的手册而喝三声!

附言还发现此提示隐藏在 How to show var_dumps in phpunit or codeception by Julian on dev.to

答案 19 :(得分:-1)

如果使用Laravel,则可以使用诸如info()之类的日志记录功能来记录到storage / logs下的Laravel日志文件。因此它不会显示在您的终端中,而是显示在日志文件中。

答案 20 :(得分:-1)

我必须modify source code才能使此代码正常工作,因此您需要将this forked repos的网址添加到编辑器中才能实现此功能

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