Этот вопрос специфичен для использования PHPUnit.
PHPUnit автоматически преобразует ошибки php в исключения. Есть ли способ проверить возвращаемое значение метода, который запускает ошибку php (либо встроенные ошибки, либо пользовательские ошибки с помощью trigger_error )?
Пример кода для проверки:
function load_file ($file) { if (! file_exists($file)) { trigger_error("file {$file} does not exist", E_USER_WARNING); return false; } return file_get_contents($file); }
Это тип теста, который я хочу написать:
public function testLoadFile () { $this->assertFalse(load_file('/some/non-existent/file')); }
Проблема, с которой я сталкиваюсь, заключается в том, что вызванная ошибка приводит к сбою моего модульного теста (как и следовало ожидать). Но если я попытаюсь поймать его или установить ожидаемое исключение, то любой код, который после срабатывания ошибки не запускается, никогда не выполняется, поэтому я не могу проверить возвращаемое значение метода.
Этот пример не работает:
public function testLoadFile () { $this->setExpectedException('Exception'); $result = load_file('/some/non-existent/file'); // code after this point never gets executed $this->assertFalse($result); }
Любые идеи, как я мог бы это достичь?
В одном модульном тесте нет возможности сделать это. Это возможно, если вы разбили тестирование возвращаемого значения и уведомление на два разных теста.
Обработчик ошибок PHPUnit ловит ошибки и уведомления PHP и преобразует их в Исключения – которые по определению останавливают выполнение программы. Функция, которую вы тестируете, никогда не возвращается вообще. Тем не менее, вы можете временно отключить преобразование ошибок в исключения, даже во время выполнения.
Это, вероятно, проще с примером, так что вот как выглядят два теста:
public function testLoadFileTriggersErrorWhenFileNotFound() { $this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is $result = load_file('/some/non-existent/file'); } public function testLoadFileRetunsFalseWhenFileNotFound() { PHPUnit_Framework_Error_Warning::$enabled = FALSE; $result = load_file('/some/non-existent/file'); $this->assertFalse($result); }
Это также дает дополнительный бонус, чтобы сделать ваши тесты более четкими, чистыми и самодокументирующими.
Re: Комментарий: Это отличный вопрос, и я понятия не имел, пока не проведу пару тестов. Похоже, что он не восстановит значение по умолчанию / оригиналу, по крайней мере, начиная с PHPUnit 3.3.17 (текущий стабильный выпуск прямо сейчас).
Итак, я бы на самом деле внес изменения в приведенное выше, чтобы выглядеть так:
public function testLoadFileRetunsFalseWhenFileNotFound() { $warningEnabledOrig = PHPUnit_Framework_Error_Warning::$enabled; PHPUnit_Framework_Error_Warning::$enabled = false; $result = load_file('/some/non-existent/file'); $this->assertFalse($result); PHPUnit_Framework_Error_Warning::$enabled = $warningEnabledOrig; }
Re: Второй комментарий:
Это не совсем так. Я смотрю на обработчик ошибок PHPUnit, и он работает следующим образом:
E_WARNING
, используйте PHPUnit_Framework_Error_Warning
как класс исключения. E_NOTICE
или E_STRICT
, используйте PHPUnit_Framework_Error_Notice
PHPUnit_Framework_Error
в качестве класса исключения используйте PHPUnit_Framework_Error
. Итак, да, ошибки E_USER_*
не превращаются в класс PHPUnit * _Warning или * _Notice, они все равно преобразуются в общее исключение PHPUnit_Framework_Error
.
Дальнейшие мысли
Хотя это зависит именно от того, как эта функция используется, я бы, вероятно, переключился на то, чтобы исключить фактическое исключение вместо запуска ошибки, если бы это был я. Да, это изменит логический поток метода и код, который использует этот метод … прямо сейчас выполнение не останавливается, когда он не может прочитать файл. Но это зависит от вас, чтобы решить, действительно ли запрашиваемый файл не является поистине исключительным поведением. Я предпочитаю использовать исключения больше, чем ошибки / предупреждения / уведомления, потому что их легче обрабатывать, тестировать и работать в потоке приложения. Обычно я резервирую уведомления о таких вещах, как отказ от метода вызова и т. Д.
Используйте phpunit.xml
конфигурации phpunit.xml
и отключите уведомление / предупреждение / ошибку для исключения. Подробнее в руководстве . В основном это примерно так:
<phpunit convertErrorsToExceptions="false" convertNoticesToExceptions="false" convertWarningsToExceptions="false"> </phpunit>
Вместо того, чтобы ожидать генерического « Exception
», как насчет ожидания « PHPUnit_Framework_Error
»?
Что-то вроде этого может сделать:
/** * @expectedException PHPUnit_Framework_Error */ public function testFailingInclude() { include 'not_existing_file.php'; }
Которая, я полагаю, также может быть написана так:
public function testLoadFile () { $this->setExpectedException('PHPUnit_Framework_Error'); $result = load_file('/some/non-existent/file'); // code after this point never gets executed $this->assertFalse($result); }
Дополнительные сведения см. В разделе Тестирование ошибок PHP
Особенно это говорит (цитирует):
PHPUnit_Framework_Error_Notice
иPHPUnit_Framework_Error_Warning
представляют собой уведомления и предупреждения PHP, соответственно.
Глядя на файл /usr/share/php/PHPUnit/TextUI/TestRunner.php, который у меня есть в моей системе, я вижу это (строка 198 и следующий) :
if (!$arguments['convertNoticesToExceptions']) { PHPUnit_Framework_Error_Notice::$enabled = FALSE; } if (!$arguments['convertWarningsToExceptions']) { PHPUnit_Framework_Error_Warning::$enabled = FALSE; }
Может быть, вам придется пройти какой-то параметр, чтобы активировать это поведение? Но, по-видимому, он включен по умолчанию …
На самом деле есть способ проверить как возвращаемое значение, так и исключенное (в данном случае, ошибку, преобразованную PHPUnit).
Вам просто нужно сделать следующее:
public function testLoadFileTriggersErrorWhenFileNotFound() { $this->assertFalse(@load_file('/some/non-existent/file')); $this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is load_file('/some/non-existent/file'); }
Обратите внимание, что для проверки возвращаемого значения вы должны использовать оператор подавления ошибок при вызове функции (перед именем функции). Таким образом, исключение не будет выбрано, и выполнение будет продолжено. Затем вы должны установить ожидаемое исключение, как обычно, для проверки ошибки.
То, что вы не можете сделать, это проверить несколько исключений в рамках единичного теста.