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


151

在运行PHPUnit测试时,我希望能够转储输出,以便可以调试一两个东西。

我已经尝试了以下内容(类似于PHPUnit Manual示例);

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版本的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错误?


1
调用该testOutput()方法的代码在哪里?
德里克·塔克

您非常拼命地尝试(echo,print,print_r,var_dump-基本上都是“输出”),通常我在进行测试输出时没有问题。您可以检查是否启用了输出缓冲:php.net/manual/en/function.ob-get-level.php-强制“测试”的最安全方法是引发异常BTW。
hakre 2011年

3
@DerrickTucker PHPUnit通过调用来完成此操作phpunit /path/to/tests/theTest.php(如果上述类在文件中theTest.php)。
杰西·特尔福德

@hakre ob_get_level()返回1。但是,这与以下代码矛盾:while (ob_get_level() > 0) { ob_end_flush(); }哪个错误ob_end_clean(): failed to delete buffer. No buffer to delete.。越来越好奇。
杰西·特尔福德

1
据说是phpunit的代码触发了错误-显然是因为phpunits输出吞咽处于活动状态(但您将其破坏了)。准确地看,函数名称也有所不同。
hakre 2011年

Answers:


196

更新

刚刚意识到实现此目的的另一种方法比--verbose命令行选项要好得多:

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

这样,您可以随时将任何内容转储到控制台,而无需使用--verboseCLI选项附带的所有不需要的输出。


正如其他答案所指出的那样,最好使用以下内置方法来测试输出:

$this->expectOutputString('foo');

但是,有时调皮并从测试用例中查看一次/临时调试输出会很有帮助。但是,无需采取var_dump破解/解决方法。通过--verbose在运行测试套件时设置命令行选项,可以轻松实现此目的。例如:

$ phpunit --verbose -c phpunit.xml

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

请参阅:为PHPUnit编写测试-测试输出


5
抱歉,错过了我们写信给stderr。确实有效。我只是被迫使用file_put_contents('php://stderr', $myDebugVar, FILE_APPEND);,因为我收到了Use of undefined constant STDERR - assumed 'STDERR'带有fwrite的消息。
Serge

问题在于这似乎无法与进程隔离一起使用。
donquixote 2014年

@donquixote不足为奇,因为该测试将在另一个进程中执行,该进程的STDERR流输出可能会被丢弃……
rdlowrey 2014年

1
您也可以使用STDOUT代替STERR
克里斯(Chris)

2
是。它的工作原理和输出似乎与相同STDERR。我PHPUnit 4.5.0在Windows cmd行中使用。一条echo语句不会给出相同的结果。echo仅在显示测试结果之后才输出。fwrite(STDERR, 'string')fwrite(STDOUT,'string')产生相同的结果:显示测试结果之前的输出。
克里斯

33

更新:有关更简单的使用方法,请参见下面的rdlowrey更新fwrite(STDERR, print_r($myDebugVar, TRUE));


这种行为是故意的(如jasonbar指出的)。报告了手册的冲突状态给PHPUnit。

一种解决方法是让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.

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


33

尝试使用 --debug

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


2
这是我的正确答案。先前答案中编写的所有fwrite语句对我都不起作用。
Kim Stacks 2014年

9

这不是错误,而是非常故意的。最好的选择是写入某种日志文件,并拖尾日志以查看输出。

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

也:

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


1
如果是故意的,那么手册肯定不会给出示例吗?另外,不要尝试测试输出本身。只是使用它来查看一些结果,这些结果会导致测试在不应进行的测试中失败。
Jess Telford

如所写:在运行测试时,我通常没有问题可以回显。您可能有一些捕获输入的配置。
hakre 2011年

1
如果不是故意的,那么手册肯定不会说是
jasonbar 2011年

1
因此,似乎在文档中存在冲突。@hakre似乎给了我相同的印象(不应吞咽)-文档的哪一部分是正确的?
杰西·特尔福德

仅当--disallow-test-output(或conf文件具有beStrictAboutOutputDuringTests =“ true”)时,生成输出的测试才会失败-文档现在说“发出测试的测试,例如通过调用测试代码或被测试代码中的print,启用此检查后,将被标记为有风险。” phpunit.readthedocs.io/en/8.4/risky-tests.html#risky-tests
NULL指针,

7

我对VisualPHPUnit感到很幸运,它确实有助于显示输出。

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

TestHello结果


嗯,为什么要投票?作为在PHPUnit测试中转储调试输出的替代方法,这有什么用?
鲍勃·斯坦

1
这被否决了,因为如果有人尝试运行此命令,则会出现语法错误。一个庞大的。
Jimbo 2014年

我忘记了功能。现在已修复,测试,剪切和粘贴。谢谢@Jimbo
Bob Stein

遗憾的是,它目前与PHP 7不兼容,显然:“由于使用phpunit的方式,VisualPHPUnit目前不兼容php7。下一个主要版本将支持PHP 7”
leo

6

您应该真正考虑一下自己的意图:如果在调试以修复测试时现在需要信息,则在测试中断时下周将再次需要它。

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

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


1
我完全同意您所说的一切。我正在使用PHPUnit进行集成测试,最终将调用Google的XML API之一。通过了所有单元测试(模拟了API调用),但是最终测试(使用了实时API调用)失败了。原来是Google API的错误,但与此同时,我想转储原始HTTP响应。
杰西·特尔福德

2
如果您需要在实现此处概述的方法的同时调试代码怎么办?
David Meister 2015年

2
这就是为什么我不喜欢答案猜测用户想要做什么的原因。我在这里是因为我已经测试过等待缓存清除。有了5秒的缓存ttls,这意味着我的测试似乎挂起了〜16 秒。我只想向用户发出一些通知,不,没有错,我们只是在等待缓存超时。如果人们可以回答问题,那么具有其他用例的人也将得到答案。
user151841 '19

4

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

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


4

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

$ phpunit --verbose -c phpunit.xml 

这种方法的优点是您无需更改测试代码,可以打印字符串,var_dump或任何您希望的内容,并且只有在设置了详细模式时,它才会在控制台中显示。

我希望这有帮助。


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

3

有点黑,但可以正常工作:引发一个异常,将调试输出作为其消息。

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

产量:

...
There was 1 error:

1) theTest::testOutput
Exception: hello

2

这取自有关Fixtures的PHPUnit文档

这应该允许您在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;
    }
}
?>

1

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

var_dump($array);
ob_flush();

还有第二种PHP方法

flush() 

我没有尝试过。


1

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

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

0

我必须修改源代码才能使此代码正常工作,因此您需要将此分叉存储库的 URL添加到作曲家,这样才能正常工作

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

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上,要实时流式传输syslog消息,请运行:

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

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

    注意:除了STDERR,您还可以指定文件名。

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

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

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

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

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

要仅在详细或调试模式下打印以上消息,请参阅: 是否有办法确定在测试中是否将--debug或--verbose传递给了PHPUnit?


尽管如果测试输出本身就是测试的一部分,请签出:测试输出文档页面。


-1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.