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

Этот вопрос специфичен для использования 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'); } 

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

То, что вы не можете сделать, это проверить несколько исключений в рамках единичного теста.