Intereting Posts

PHPUnit – Unit Testing с элементами, которые должны отправлять заголовки

В настоящее время я работаю с PHPUnit, чтобы попытаться разработать тесты вместе с тем, что я пишу, однако в настоящее время я работаю над написанием диспетчера сеансов, и у меня возникают проблемы с этим …

Конструктор класса обработки сеанса

private function __construct() { if (!headers_sent()) { session_start(); self::$session_id = session_id(); } } 

Тем не менее, поскольку PHPUnit отправляет текст перед началом тестирования, любое тестирование этого объекта возвращает неудачный тест, поскольку HTTP-заголовки были отправлены …

Solutions Collecting From Web of "PHPUnit – Unit Testing с элементами, которые должны отправлять заголовки"

Ну, ваш менеджер сессии в основном разбит по дизайну. Чтобы быть в состоянии проверить что-то, должно быть возможно изолировать его от побочных эффектов. К сожалению, PHP разработан таким образом, что он поощряет либеральное использование глобального состояния ( echo , header , exit , session_start и т. Д.).

Лучшее, что вы можете сделать, это изолировать побочные эффекты в компоненте, который можно поменять местами во время выполнения. Таким образом, ваши тесты могут использовать смешные объекты, в то время как в реальном коде используются адаптеры, которые имеют реальные побочные эффекты. Вы обнаружите, что это не очень хорошо сочетается с синглонами, которые, как я полагаю, вы используете. Таким образом, вам придется использовать какой-то другой механизм для распространения общих объектов на ваш код. Вы можете начать с статического реестра, но есть даже лучшие решения, если вы не возражаете немного учиться.

Если вы не можете этого сделать, у вас всегда есть возможность написать интеграционные тесты. Например. используйте эквивалент PHPUnit для WebTestCase .

Создайте загрузочный файл для phpunit, который вызывает:

 session_start(); 

Затем запустите phpunit следующим образом:

 phpunit --bootstrap pathToBootstrap.php --anotherSwitch /your/test/path/ 

Файл bootstrap вызывается перед всем остальным, поэтому заголовок не отправляется, и все должно работать нормально.

phpUnit печатает выходные данные при запуске тестов, в результате чего headers_sent () возвращает true даже в вашем первом тесте.

Чтобы преодолеть эту проблему для всего набора тестов, вам просто нужно использовать ob_start () в вашем сценарии установки.

Например, скажем, у вас есть файл с именем AllTests.php, который первым загружается phpUnit. Этот скрипт может выглядеть следующим образом:

 <?php ob_start(); require_once 'YourFramework/AllTests.php'; class AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('YourFramework'); $suite->addTest(YourFramework_AllTests::suite()); return $suite; } } - <?php ob_start(); require_once 'YourFramework/AllTests.php'; class AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('YourFramework'); $suite->addTest(YourFramework_AllTests::suite()); return $suite; } } 

У меня была такая же проблема, и я решил ее, вызвав phpunit с флагом -stderr так же, как это:

 phpunit --stderr /path/to/your/test 

Надеюсь, это поможет кому-то!

Я думаю, что «правильным» решением является создание очень простого класса (так просто, что его не нужно тестировать), это оболочка для функций, связанных с сеансом PHP, и использовать его вместо прямого вызова session_start() и т. Д.

В тестовом прохождении макет объекта вместо реального состояния, непроверяемого класса сеанса.

 private function __construct(SessionWrapper $wrapper) { if (!$wrapper->headers_sent()) { $wrapper->session_start(); $this->session_id = $wrapper->session_id(); } } 

Мне интересно, почему никто не перечислил вариант XDebug:

 /** * @runInSeparateProcess * @requires extension xdebug */ public function testGivenHeaderIsIncludedIntoResponse() { $customHeaderName = 'foo'; $customHeaderValue = 'bar'; // Here execute the code which is supposed to set headers // ... $expectedHeader = $customHeaderName . ': ' . $customHeaderValue; $headers = xdebug_get_headers(); $this->assertContains($expectedHeader, $headers); } 

Не можете ли вы использовать буферизацию вывода перед началом теста? Если вы буферизируете все, что выводится, у вас не должно быть проблем с настройкой каких-либо заголовков, поскольку в этот момент клиент еще не будет отправлен.

Даже если OB используется где-то внутри ваших классов, он стекируется, и OB не должен влиять на то, что происходит внутри.

Насколько я знаю, Zend Framework использует ту же буферизацию вывода для своих тестов пакета Zend_Session. Вы можете взглянуть на их тестовые примеры, чтобы начать работу.

Создание файла bootstrap, указывающего на 4 сообщения назад, кажется самым чистым способом.

Часто с PHP мы должны поддерживать и пытаться добавить какую-то инженерную дисциплину к старым проектам, которые ужасно сведены вместе. У нас нет времени (или авторитета), чтобы вырвать всю груду мусора и начать снова, поэтому первый ответ от troelskn не всегда возможен, как способ продвижения вперед. (Если бы мы могли вернуться к первоначальному дизайну, то мы могли бы полностью протолкнуть PHP и использовать что-то более современное, например ruby ​​или python, а не помогать увековечить этот COBOL в мире веб-разработки.)

Если вы пытаетесь написать модульные тесты для модулей, которые используют session_start или setcookie на всем протяжении, чем запуск сеанса в файле boostrap, вы можете обойти эти проблемы.

Поскольку я сейчас не хочу запускать мой загрузочный блок (да, я знаю, что большинство из вас этого не делает), я столкнулся с той же проблемой (как header (), так и session_start ()). Решение, которое я нашел, довольно просто, в вашем unittest bootstrap определите константу и просто проверьте ее перед отправкой заголовка или началом сеанса:

 // phpunit_bootstrap.php define('UNITTEST_RUNNING', true); // bootstrap.php (application bootstrap) defined('UNITTEST_RUNNING') || define('UNITTEST_RUNNING', false); ..... if(UNITTEST_RUNNING===false){ session_start(); } 

Я согласен с тем, что это не идеально по дизайну, но я убираю существующее приложение, переписывание больших частей нежелательно. Я также использую ту же логику для тестирования частных методов с использованием магических методов __call () и __set ().

 public function __set($name, $value){ if(UNITTEST_RUNNING===true){ $name='_' . $name; $this->$name=$value; } throw new Exception('__set() can only be used when unittesting!'); } 

Кажется, что вам нужно ввести сеанс, чтобы вы могли проверить свой код. Лучшим вариантом, который я использовал, является Aura.Auth для процесса аутентификации и использования NullSession и NullSegment для тестирования.

Аура с нулевыми сеансами

Структура Aura прекрасно написана, и вы можете использовать Aura.Auth самостоятельно без каких-либо других зависимостей структуры Aura.