【问题标题】:Test PHP headers with PHPUnit使用 PHPUnit 测试 PHP 标头
【发布时间】:2012-04-02 10:58:36
【问题描述】:

我正在尝试使用 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 内部的东西导致了这种情况?

问题可能是什么?

【问题讨论】:

  • 如果还有其他人对此感兴趣,我只想介绍一下。 headers_list() 在运行 PHPunit(使用 PHP CLI)时不起作用,但 xdebug_get_headers() 可以替代。

标签: php unit-testing phpunit output-buffering


【解决方案1】:

问题是 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

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

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

【讨论】:

  • 这会导致一些 define() 语句中的错误 PHPUnit_Framework_Exception: 注意:Constant xyz already defined
  • @mebjas 这听起来不相关。
  • 我在申请时得到了这个:PHP Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'SplFileInfo' is not allowed' in phar:///usr/local/bin/phpunit/phpunit/Util/GlobalState.php:211
  • 这绝对是解决问题的最佳选择。它就像一个魅力!
  • 我必须使用 xdebug_get_headers() 来获取设置标题的数组。 headers_list() 全局函数在我的情况下不起作用。
【解决方案2】:

虽然在单独的进程中运行测试确实可以解决问题,但在运行大量测试时会产生明显的开销。

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

phpunit --stderr <options>

这应该可以解决问题,这也意味着您不必创建包装函数并替换代码中的所有匹配项。

【讨论】:

  • 太棒了!我的代码没有变化,没有单独的进程,它可以工作。
  • 但我仍然会看到我犯的错误并得到 error_reporting(E_ALL) 的输出吗??
  • @spankmaster79 是的,它只会进入终端的标准错误。默认情况下,大多数终端将标准输出和标准错误一起打印,但它们实际上是单独的流。
  • 我被造了!!! tnx 这个技巧,确实有道理,错误应该报告给stderror!!! Tnx
  • 您可以在 phpunit.xml 中添加stderr="true" 以节省一些击键次数。
【解决方案3】:

顺便说一句:对我来说headers_list() 一直返回 0 个元素。我注意到@titel 对这个问题的评论,并认为这里值得特别提及:

如果有其他人感兴趣,只是想介绍一下 在这方面也是如此。 headers_list() 在运行 PHPunit 时不起作用 (使用 PHP CLI)但 xdebug_get_headers() 可以代替。

HTH

【讨论】:

    【解决方案4】:

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

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

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

    【讨论】:

    • 最好只为需要它的测试定义它。为所有测试设置它只会使运行测试变慢。
    【解决方案5】:

    我有一个更激进的解决方案,以便在我的 tested/included 文件中使用$_SESSION。 我在 ../PHPUnit/Utils/Printer.php 编辑了一个 PHPUnit 文件,在 命令“print $buffer”。

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

    【讨论】:

    • 您是否在存储库中提交了拉取请求?我想这可能是一个常见的问题。
    • 感谢您的提示,我在 phpunit bootstrap.php 中调用了 session_start(),它对我有用
    【解决方案6】:

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

    name@host [~/test]# phpunit --process-isolation HeadersTest.php
    

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

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

    【讨论】:

    • 这可能是追踪原因并进行一些测试的第一步,但为了创建可维护的测试,只需将注释直接嵌入到测试文件中即可。命令行中需要的“特殊”选项越少,CI 系统配置的维护就越容易。
    • 谁降级了#fail。这个答案作为另一种选择是正确的。
    【解决方案7】:

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

    phpunit --stderr
    

    【讨论】:

      【解决方案8】:

      如果你使用 Laravel 并且在路由文件中添加了一些头文件

      那么你需要用 headers_sent 包围以在测试期间忽略

      这是一个例子:

      if (!headers_sent()) {
          header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
          header('Access-Control-Allow-Headers: Origin, Access-Control-Allow-Origin,  Content-Type, X-Authorization, Authorization, Accept,charset,boundary,Content-Length');
          header('Access-Control-Allow-Origin: *');
      }
      

      然后再次尝试单元测试它会通过..

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-11-26
        • 2017-07-11
        • 1970-01-01
        • 2011-10-10
        • 2015-10-17
        • 2012-09-08
        • 2012-12-18
        • 2012-07-16
        相关资源
        最近更新 更多