【问题标题】:PhpStorm, PHPUnit and setcookiePhpStorm、PHPUnit 和 setcookie
【发布时间】:2011-07-25 03:24:24
【问题描述】:

我正在尝试在一个相当不错的 IDE PhpStorm 中使用 setcookie() 函数进行一些单元测试。但我每次都会收到以下错误:

Cannot modify header information - headers already sent by (output started at /tmp/phpunit.php:418)

这个错误的原因可能是 print('some text') 在 setcookie() 调用之前使用了 flush()。但是刷新是在 PhpStorm 生成的 /tmp/phpunit.php 文件中执行的。虽然 setcookie() 是从我的来源调用的。所以我不能编辑生成的文件来做某种输出缓冲。还有另一个时刻:PhpStorm 像这样执行 /tmp/phpunit.php 脚本:

/usr/bin/php /tmp/phpunit.php -config /var/www/.../protected/tests/phpunit.xml d /var/www/.../protected/tests/unit/user

请帮我解决这个问题。如何直接从 PhpStorm 运行单元测试?

【问题讨论】:

标签: php phpunit phpstorm


【解决方案1】:

解决此问题的一种可能方法是使用“模拟”替换 setcookie() 函数。

这是单元测试中的一种常用技术,您希望测试依赖于您不想影响当前测试的外部类或函数的东西。

这样做的方法是在您的单元测试代码中为setcookie() 创建一个存根函数定义。然后将在测试期间调用它而不是真正的 setcookie() 函数。具体如何实现这个存根函数取决于您,并且取决于您的代码使用它的目的。

这种方法的主要问题是 PHP 不允许您默认覆盖现有函数 - 如果您在标准 PHP 安装上尝试此操作,您将收到“错误:无法重新声明函数” .

这个问题的解决方案是PHP的Runkit extension,它是专门为这种测试设计的,并且允许你rename an existing function,包括内置的。

如果您在测试环境中配置 PHP 安装以包含 Runkit 扩展,您将能够进行此类测试。

希望对您有所帮助。

【讨论】:

  • 非常感谢您的回答,斯普德利!这真是个好主意。我将很快在我的测试环境中试用 Runkit。
  • 如果您不想或不能使用 RunKit(即使可以),我建议创建一个封装所有 cookie 操作的类。然后,您可以为该类创建一个模拟对象,以避免完全调用 setcookie()
  • 感谢您的回答,大卫!看来你的观点更适用。但是我已经通过像这样重新定义setcookie() 来成功使用Runkit:runkit_function_redefine('setcookie', '$name, $value = null, $expire = null, $path = null, $domain = null, $secure = null, $httponly = null', '');
  • 链接是404。
【解决方案2】:

我找到了一个更简单的解决方案。考虑这个类:

class Cookie
{
    public function set($name, $value)
    {
        return setcookie($name, $value);
    }
}

测试会抛出问题中描述的错误,除非你设置注解@runInSeparateProcess

class CookieTest
{
    /**
     * @runInSeparateProcess
     */
    function test_set()
    {
        $cookie = new Cookie;
        $this->assertTrue($cookie->set('mycookie', 'myvalue');
    }
}

【讨论】:

  • 测试将失败,因为Cookie::set 返回 void。
  • @JohnLinhart 这只是抑制错误,但实际上并未在(单独的过程)中设置该值。所以你不能测试代码并断言 $_COOKIE['mycookie'] == 'myvalue'。你会得到一个未定义的索引错误。
【解决方案3】:

而不是尝试设置实际的 cookie 标头(这将失败,因为内容已经发送);出于测试目的,您可以简单地显式设置 COOKIE 超全局:

$_COOKIE['mycookie']=myvalue';

【讨论】:

  • 投了反对票,因为它不是一个有效的解决方案,但现在无法撤消。实际上,这对我来说现在与 @runInSeparateProcess 注释结合使用。否则仍然会弹出“标头已发送”错误。
【解决方案4】:

另一种解决方法,无需创建代理类并模拟它,但需要更改工作代码级别。

function setTestableCookie(string $key, $value): void
{
    $_COOKIE[$key] = $value;

    if (headers_sent() === false) {
        setcookie(
          $key,
          $value,
          ... other cookie params ...
        );
    }
}

这样它将在单元测试中工作并在生产代码上设置适当的 cookie 参数。

【讨论】:

    猜你喜欢
    • 2012-08-01
    • 2012-09-30
    • 2014-12-28
    • 2018-03-24
    • 2016-10-21
    • 2016-01-04
    • 2016-01-22
    • 2015-09-19
    • 2017-03-15
    相关资源
    最近更新 更多