PhpStorm, PHPUnit и setcookie

Я пытаюсь сделать несколько модульных тестов, используя функцию setcookie() в довольно хорошей среде IDE PhpStorm. Но я получаю следующую ошибку каждый раз:

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

Вероятно, причиной этой ошибки является print('some text') с flush() перед вызовом setcookie(). Но сброс выполняется в файле /tmp/phpunit.php, сгенерированном PhpStorm. Хотя 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?


person alexey_detr    schedule 29.03.2011    source источник
comment
Я использовал следующее: phpunit --stderr ‹options› Это работало при использовании setcookie. stackoverflow.com/questions/9745080/   -  person HMR    schedule 26.11.2012


Ответы (4)


Один из возможных способов обойти это — использовать «фиктивную» замену функции setcookie().

Это распространенный метод модульного тестирования, когда вы хотите протестировать что-то, что зависит от внешнего класса или функции, которые вы не хотите влиять на текущий тест.

Способ сделать это - создать определение функции-заглушки для setcookie() в коде модульного теста. Затем это будет вызываться во время теста вместо реальной функции setcookie(). Как именно вы реализуете эту функцию-заглушку, зависит от вас и от того, для чего она используется вашим кодом.

Основная проблема, с которой вы столкнетесь при таком подходе, заключается в том, что PHP не позволяет вам переопределять существующие функции по умолчанию — если вы попробуете это на стандартной установке PHP, вы получите «ошибка: функция не может быть переобъявлена».

Решением этой проблемы является PHP-расширение Runkit, специально предназначенное для такого рода тестирования и позволяющее вам переименовать существующую функцию, включая встроенные.

Если вы настроите установку PHP в своей тестовой среде для включения расширения Runkit, вы сможете выполнить такой тест.

Надеюсь, это поможет.

person Spudley    schedule 29.03.2011
comment
Большое спасибо за ответ, Спадли! Это действительно хорошая идея. Я собираюсь попробовать Runkit в своей тестовой среде в ближайшее время. - person alexey_detr; 29.03.2011
comment
Если вы не хотите или не можете использовать RunKit (даже если можете), я рекомендую создать класс, инкапсулирующий все манипуляции с файлами cookie. Затем вы можете создать фиктивный объект для этого класса, чтобы полностью избежать вызова setcookie(). - person David Harkness; 29.03.2011
comment
Спасибо за ответ, Давид! Кажется, что ваша точка зрения даже более применима. Но я уже добился успеха с Runkit, переопределив setcookie() следующим образом: runkit_function_redefine('setcookie', '$name, $value = null, $expire = null, $path = null, $domain = null, $secure = null, $httponly = null', ''); - person alexey_detr; 30.03.2011

Я нашел более простое решение. Рассмотрим этот класс:

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');
    }
}
person John Linhart    schedule 27.04.2017
comment
Тест завершится ошибкой, потому что Cookie::set возвращает void. - person amphetamachine; 26.07.2017
comment
@JohnLinhart Это просто подавление ошибки, но значение фактически не устанавливается в (отдельный процесс). Таким образом, вы не можете тестировать код и утверждать $_COOKIE['mycookie'] == 'myvalue'. Вы получите неопределенную ошибку индекса. - person Smamatti; 26.10.2019

Еще один обходной путь без создания прокси-класса и издевательства над ним, но требует изменения на уровне рабочего кода.

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

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

Таким образом, он будет работать в модульных тестах и ​​устанавливать правильные параметры файлов cookie в производственном коде.

person Konrad Gałęzowski    schedule 24.06.2020

Вместо того, чтобы пытаться установить фактические заголовки файлов cookie (что не удастся, поскольку контент уже отправлен); В целях тестирования вы можете просто явно указать суперглобальный файл COOKIE:

$_COOKIE['mycookie']=myvalue';
person matwr    schedule 26.12.2018
comment
Проголосовал за это, так как это не было рабочим решением, но сейчас это нельзя отменить. На самом деле это работает для меня сейчас в сочетании с аннотацией @runInSeparateProcess. В противном случае по-прежнему выскакивала ошибка «заголовок уже отправлен». - person Smamatti; 27.10.2019