Игнорировать exit () и die () с PHPUnit

Прежде всего, заголовок предполагает, что это дубликат this или this, но по нескольким причинам эти ответы не работают для меня, даже если моя первоначальная проблема такая же. Я объясню почему.

Моя проблема заключается в следующем: у меня есть несколько случаев в моем коде, когда я хочу отправить заголовок и тело, а затем прекратить обработку. В отличие от этих других вопросов, я не могу использовать return или вместо этого генерировать исключение (это, очевидно, разные функции, предназначенные для других целей, чем exit, и это не ошибка; это просто раннее завершение времени выполнения в некоторых конкретных случаях).

Тем не менее, я хочу написать модульные тесты, запускающие эти методы, убедитесь, что установлены соответствующие заголовки (решение найдено здесь), тело вывода правильное (решено с помощью $this->expectOutputString()-метода в тестовом примере), а затем продолжить тестирование. Между тем произойдет exit.

Я пробовал @runInSeparateProcess-аннотацию в PHPUnit, я также проверил test_helpers, которое работает, но я не хочу добавлять еще одно расширение (имейте в виду, что тесты будут запускаться и в производственной среде) для одной единственной строки собственного кода PHP, которая все ломает. Должен быть более простой способ без ущерба для лучших практик.

У кого-нибудь есть хорошее решение этой проблемы?


person Helge Talvik Söderström    schedule 28.05.2014    source источник
comment
Вы нашли какое-нибудь решение?   -  person gmponos    schedule 24.03.2016


Ответы (2)


Я добавил в начальную загрузку переменную, на которую я мог ссылаться в коде с IF в тех редких случаях, когда мне не нужно выходить.

define ('PHPUNIT_RUNNING', 1); 

Обычная программа:

if(! @PHPUNIT_RUNNING === 1 )
{
    exit;
}

PHPUnit как правило не определяется, поэтому при выполнении PHP генерируется предупреждение (которое мы скрываем с помощью @. Затем код будет делать то, что мы хотим, когда не в тестовом режиме. Это было добавлено после того, как основной код был написан как мы добавили тестирование PHPUnit в существующий проект вместо выполнения TDD.

Обратите внимание:

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

person Steven Scott    schedule 28.05.2014
comment
Спасибо за ответ, Стивен. Да, в моем запасном решении для решения этой проблемы просто используется переменная среды тестирования, но мне не нравится наличие специального кода, заставляющего тесты работать (if(!Environment::isTesting()) { exit; }). В настоящее время я использую test_helpers, который позволяет мне запускать специфический метод set_exit_overload() для перегрузки выхода любой функцией (т.е. пустой). - person Helge Talvik Söderström; 28.05.2014
comment
Я тоже ненавидел это, но, к сожалению, я не мог придумать более простого способа обойти это. Старая база кода, которая у нас была, вызвала проблему с людьми, которые выбирали простой выход в коде и использовали Exit вместо того, чтобы пытаться вернуть или выбросить исключения. Более процедурный код, чем истинный объектно-ориентированный подход. - person Steven Scott; 28.05.2014

Я только что проверил следующую идею:

ExitException.php:

<?php
class DieException extends Exception {} // for those people who like die as well as exit
class ExitException extends Exception {}

entrypoint.php:

<?php
require_once 'ExitException.php';

try {
  require 'main_code.php';
  run_main_code();
}
catch (DieException $e) {
  return $e->getMessage();
}
catch (ExitException $e) {
  return $e->getMessage();
}

Спустя несколько includeс:

deepcode.php

<?php
throw new ExitException('Exiting normally');

Это имитирует exit или die, пойманный на самом верхнем уровне. Ваша программа будет завершена, как и раньше, и теперь ее можно тестировать, не убивая весь набор тестов. Единственная проблема заключается в том, что вы также поймаете другие простые Exception в своем существующем коде, и в этом случае вам придется изменить свой код, чтобы повторно выбросить _8 _ / _ 9_, пока они не достигнут верхнего уровня.

Другая альтернатива - вернуться в чистом виде, что, вероятно, потребует переписывания большого количества кода. То есть, если вы не завершили работу в этот момент, почему ваша программа будет генерировать дальнейший вывод? Он должен как можно раньше проверить, требуется ли «ранний выход», а затем просто вызвать этот код и перейти к естественному концу программы.

Эта проблема усложняется, если вам приходится иметь дело со сторонними библиотеками или другим кодом, который вы не должны или не можете изменять, но если они не поставляются со своими собственными модульными тестами, для них нет смысла писать тесты, потому что в идеале , вам не следует изменять сторонний код, чтобы избежать проблем с совместимостью в будущем. Хорошо написанная сторонняя библиотека никогда не должна die. Он всегда должен возвращать управление вызывающей программе или выдавать Exception, который можно поймать.

person CJ Dennis    schedule 22.12.2016