Проверьте заголовки PHP с помощью PHPUnit

Я пытаюсь использовать 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 вызывает это?

В чем может быть проблема?


person titel    schedule 16.03.2012    source источник
comment
Просто хотел осветить это, если есть другие люди, которые тоже заинтересованы в этом. headers_list () не работает при запуске PHPunit (который использует PHP CLI), но вместо этого работает xdebug_get_headers ().   -  person titel    schedule 18.03.2012


Ответы (8)


Проблема в том, что 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');
}
person SamHennessy    schedule 17.03.2012
comment
это вызывает ошибки в некоторых операторах define () PHPUnit_Framework_Exception: Примечание: константа xyz уже определена - person Minhaz; 03.07.2014
comment
@mebjas Это звучит не связанно. - person SamHennessy; 03.07.2014
comment
Я имел в виду, что если мне нужно протестировать заголовки в phpunit, и у меня есть define операторы в моем коде, этот метод вызывает ошибки, как я уже упоминал. - person Minhaz; 05.07.2014
comment
@mebjas, добавив if с defined () - это, возможно, то, что вам нужно добавить вокруг вашего определения. Если вам нужно изменить значение ваших констант для тестирования вашего кода, у вас возникнут проблемы. - person SamHennessy; 09.07.2014
comment
Получил при применении: 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 - person t1gor; 05.01.2015
comment
@ t1gor попробуйте добавить в команду --no-globals-backup. См. phpunit.de/manual/current/en/ - person SamHennessy; 06.01.2015
comment
@SamHennessy, глобальное резервное копирование отключено для всех моих тестов, но спасибо. Есть другие идеи? - person t1gor; 06.01.2015
comment
Это определенно лучший вариант решения проблемы. Оно работало завораживающе! - person xarlymg89; 12.06.2015
comment
Мне пришлось использовать xdebug_get_headers (), чтобы получить массив установленных заголовков. В моем случае глобальная функция headers_list () не работала. - person Shalom Sam; 08.11.2015
comment
У меня проблема с уже определенными определениями и константой. Оказалось, что в моем phpunit config.php есть define (). Заключение их в if-defined решило эту проблему. Похоже, что конфигурационный файл загружается дважды, что кажется разумным, если подумать. - person Mikael Roos; 07.12.2015
comment
Это привело к зависанию тестов на неопределенное время. - person fraxture; 29.02.2016
comment
@fraxture какая именно часть? Вы про ответ или один из комментариев. - person SamHennessy; 01.03.2016
comment
для старой нити. Я попробовал это решение, но, к сожалению, тест не прошел полностью, хотя он эхо OK (1 test, 0 assertions). Я заметил это, отслеживая код. У меня возникла проблема с session_start() Cannot send session cookie - headers already sent. Я думаю, что он остановил тест сразу после ошибки. и продолжил. потому что тест не прошел по моей трассировке. - person xCHAN; 27.10.2017

Хотя запуск теста в отдельном процессе действительно решает проблему, при запуске большого набора тестов возникают заметные накладные расходы.

Мое исправление заключалось в том, чтобы направить вывод phpunit на stderr, например:

phpunit --stderr <options>

Это должно решить проблему, а также означает, что вам не нужно создавать функцию-оболочку и заменять все вхождения в вашем коде.

person Jon Cairns    schedule 30.05.2012
comment
Блестяще! Никаких изменений в моем коде, никаких отдельных процессов, и он работает. - person alexfernandez; 28.10.2012
comment
но смогу ли я по-прежнему видеть сделанные мной ошибки и вывод error_reporting (E_ALL) ?? - person spankmaster79; 02.04.2013
comment
@ spankmaster79 да, просто перейдет к стандартной ошибке вашего терминала. По умолчанию большинство терминалов выводят стандартный вывод и стандартную ошибку вместе, но на самом деле это отдельные потоки. - person Jon Cairns; 02.04.2013
comment
я становлюсь сделан !!! tnx для этого трюка, это имеет смысл, об ошибках следует сообщать в stderror !!! Tnx - person th3n3rd; 20.09.2013
comment
Вы можете добавить stderr="true" в свой phpunit.xml, чтобы сэкономить несколько нажатий клавиш. - person tszming; 27.08.2014
comment
Я знаю, что это старый поток, но проблема, с которой я столкнулся с этим решением, заключается в том, что он не создает файл junit.xml из phpunit.xml для моего модульного теста. Я думаю, это потому, что он выводит на stderr вместо stdout. - person xCHAN; 26.10.2017

В стороне: для меня headers_list() продолжал возвращать 0 элементов. Я заметил комментарий @titel по этому вопросу и решил, что он заслуживает особого упоминания здесь:

Просто хотел осветить это, если есть другие люди, которые тоже заинтересованы в этом. headers_list() не работает при запуске модуля PHP (который использует PHP CLI), но вместо этого работает xdebug_get_headers().

HTH

person Melle    schedule 06.10.2016

Как уже упоминалось в комментарии, я думаю, что это лучшее решение для определения processIsolation в файле конфигурации XML, например

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

Таким образом, вам не нужно передавать параметр --stderr, который может раздражать ваших коллег.

person PepeNietnagel    schedule 12.06.2017
comment
Вероятно, лучше определить его только для теста, который этого требует. Установка этого параметра для всех тестов просто замедлит выполнение тестов. - person Shi; 03.09.2017

У меня было более радикальное решение: использовать $_SESSION в своих протестированных / включенных файлах. Я отредактировал один из файлов PHPUnit в ../PHPUnit/Utils/Printer.php, чтобы перед командой «print $ buffer» .

Это сработало для меня как шарм. Но я считаю, что решение пользователя "joonty" на данный момент лучшее из всех.

person Sergio Abreu    schedule 09.11.2013
comment
Вы отправили запрос на перенос в репозиторий? Думаю, это может быть обычная проблема. - person t1gor; 05.01.2015
comment
Спасибо за подсказку, я вызвал session_start () в моем phpunit bootstrap.php, и он работает для меня - person bumperbox; 08.06.2015

Альтернативным решением для @runInSeparateProcess является указание параметра --process -olated при запуске PHPUnit:

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

Это аналогично установке параметра processIsolation = "true" в phpunit.xml.

Это решение имеет те же преимущества / недостатки, что и указание опции --stderr, которая, однако, не сработала в моем случае. По сути, никаких изменений кода не требуется, даже если производительность может снизиться из-за запуска каждого теста в отдельном процессе PHP.

person AlexB    schedule 23.01.2016
comment
Это может быть первым шагом в отслеживании причины и выполнении некоторых тестов, но для создания поддерживаемых тестов просто вставьте аннотацию непосредственно в тестовый файл. Чем меньше «специальных» параметров требуется в командной строке, тем проще будет поддерживать конфигурацию системы CI. - person Shi; 03.09.2017
comment
кто хоть раз понижал #fail. этот ответ верен как еще один вариант. - person f b; 19.06.2018

Используйте параметр --stderr для получения заголовков из PHPUnit после ваших тестов.

phpunit --stderr
person NSukonny    schedule 18.02.2020

если вы используете 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: *');
}

затем попробуйте снова выполнить модульный тест, он пройдет.

person Alaa Moneam    schedule 23.05.2021