使用PHPUnit测试PHP头文件

时间:2012-03-16 22:04:21

标签: php unit-testing phpunit output-buffering

我正在尝试使用PHPunit来测试输出一些自定义标题的类。

问题是在我的机器上:

<?php

class HeadersTest extends PHPUnit_Framework_TestCase {

    public function testHeaders()
    {
        ob_start();

        header('Location: foo');
        $headers_list = headers_list();
        header_remove();

        ob_clean();

        $this->assertContains('Location: foo', $headers_list);
    }
}

甚至是这样:

<?php

class HeadersTest extends PHPUnit_Framework_TestCase {

    public function testHeaders()
    {
        ob_start();

        header('Location: foo');
        header_remove();

        ob_clean();
    }
}

返回此错误:

name@host [~/test]# phpunit --verbose HeadersTest.php 
PHPUnit 3.6.10 by Sebastian Bergmann.

E

Time: 0 seconds, Memory: 2.25Mb

There was 1 error:

1) HeadersTest::testHeaders
Cannot modify header information - headers already sent by (output started at /usr/local/lib/php/PHPUnit/Util/Printer.php:173)

/test/HeadersTest.php:9

FAILURES!
Tests: 1, Assertions: 0, Errors: 1.

这看起来好像在测试运行之前还有其他东西输出到终端,即使没有包含其他文件且在PHP标记开头之前没有其他字符。它可能是PHPunit中导致这种情况的东西吗?

问题是什么?

7 个答案:

答案 0 :(得分:110)

问题在于PHPUnit会在屏幕上打印标题,此时您无法添加更多标题。

解决方法是在一个独立的过程中运行测试。这是一个例子

<?php

class FooTest extends PHPUnit_Framework_TestCase
{
    /**
     * @runInSeparateProcess
     */
    public function testBar()
    {
        header('Location : http://foo.com');
    }
}

这将导致:

$ phpunit FooTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

.

Time: 1 second, Memory: 9.00Mb

OK (1 test, 0 assertions)

关键是@runInSeparateProcess注释。

如果您正在使用PHPUnit~4.1或其他内容并收到错误:

PHP Fatal error:  Uncaught Error: Class 'PHPUnit_Util_Configuration' not found in -:378
Stack trace:
#0 {main}
  thrown in - on line 378

Fatal error: Uncaught Error: Class 'PHPUnit_Util_Configuration' not found in - on line 378

Error: Class 'PHPUnit_Util_Configuration' not found in - on line 378

Call Stack:
    0.0013     582512   1. {main}() -:0

尝试将此添加到您的bootstrap文件中以修复它:

<?php
if (!defined('PHPUNIT_COMPOSER_INSTALL')) {
    define('PHPUNIT_COMPOSER_INSTALL', __DIR__ . '/path/to/composer/vendors/dir/autoload.php');
}

答案 1 :(得分:99)

尽管在单独的进程中运行测试确实解决了问题,但在运行大量测试时会出现明显的开销。

我的修复方法是将phpunit的输出定向到stderr,如下所示:

phpunit --stderr <options>

这应解决问题,这也意味着您不必创建包装函数并替换代码中的所有实例。

答案 2 :(得分:8)

暂且不说:对我而言,headers_list()一直在返回0个元素。我注意到@titel对这个问题的评论,并认为值得特别提及:

  

如果有其他人感兴趣的话,只是想覆盖这个   在这方面也是如此。运行PHPunit时headers_list()不起作用   (使用PHP CLI),但xdebug_get_headers()可以工作。

HTH

答案 3 :(得分:3)

我有一个更激进的解决方案,以便在我的测试/包含文件中使用$_SESSION。 我在 ../ PHPUnit / Utils / Printer.php 编辑了一个 PHPUnit 文件,在命令“print $ buffer”之前有一个"session_start();"即可。

它对我来说就像一个魅力。但我认为“joonty”用户的解决方案是迄今为止最好的解决方案。

答案 4 :(得分:0)

@runInSeparateProcess的另一种解决方案是在运行PHPUnit时指定--process-isolation选项:

void f(int **v, int n)
{
    int i,j;
    for(i=0; i<n; i++)
    {
        for(j=0; j<n; j++) printf("%d ", v[i][j]);
        printf("\n");
    }
}

int main()
{
    int a[100][100],n=5,i,j;
    f(a,n);
    return 0;
}

这类似于在phpunit.xml中设置processIsolation =“true”选项。

此解决方案与指定--stderr选项具有相似的优点/缺点,但在我的情况下不起作用。基本上不需要更改代码,即使由于在单独的PHP进程中运行每个测试而可能会导致性能下降。

答案 5 :(得分:0)

正如评论中已经提到的,我认为这是在XML配置文件中定义processIsolation的更好解决方案,如

     <?xml version="1.0" encoding="UTF-8"?>
     <phpunit
        processIsolation            = "true"
        // ... 
     >
     </phpunit>

像这样,您不必通过--stderr选项,这可能会激怒您的同事。

答案 6 :(得分:0)

在测试后使用--stderr参数从PHPUnit获取标头。

phpunit --stderr