Проверить возвращаемое значение метода, который вызывает ошибку с помощью 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'));
}
Проблема, с которой я столкнулась, заключается в том, что вызванная ошибка приводит к сбою моего unit test (как следует). Но если я попытаюсь поймать его или установить ожидаемое исключение, то любой код, который после срабатывания ошибки будет выполнен, никогда не будет выполняться, поэтому у меня нет возможности проверить возвращаемое значение метода.
Этот пример не работает:
public function testLoadFile ()
{
$this->setExpectedException('Exception');
$result = load_file('/some/non-existent/file');
// code after this point never gets executed
$this->assertFalse($result);
}
Любые идеи, как я мог бы это достичь?
Ответы
Ответ 1
Невозможно сделать это за один unit test. Это возможно, если вы разбили тестирование возвращаемого значения и уведомление на два разных теста.
Обработчик ошибок 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
как класс исключения.
Итак, да, ошибки E_USER_*
не превращаются в класс PHPUnit * _Warning или * _Notice, они все равно преобразуются в общее исключение PHPUnit_Framework_Error
.
Дальнейшие мысли
Хотя это зависит именно от того, как используется функция, я бы, вероятно, переключился на то, чтобы исключить фактическое исключение вместо запуска ошибки, если бы это был я. Да, это изменит логический поток метода и код, который использует этот метод... прямо сейчас выполнение не останавливается, когда он не может прочитать файл. Но вам решать, действительно ли запрашиваемый файл не является поистине исключительным поведением. Я предпочитаю использовать исключения больше, чем ошибки/предупреждения/уведомления, потому что их легче обрабатывать, тестировать и работать в потоке приложения. Я обычно резервирую уведомления для таких вещей, как вызовы с обесцененными вызовами и т.д.
Ответ 2
Используйте конфигурационный файл phpunit.xml
и отключите уведомление/предупреждение/ошибку для исключения. Подробнее подробная информация в руководстве. Это в основном что-то вроде этого:
<phpunit convertErrorsToExceptions="false"
convertNoticesToExceptions="false"
convertWarningsToExceptions="false">
</phpunit>
Ответ 3
Вместо того, чтобы ожидать общий "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;
}
Значит, вам придется пройти какой-то параметр, чтобы активировать это поведение? Но он по умолчанию включен...
Ответ 4
На самом деле есть способ протестировать как возвращаемое значение, так и исключенное (в этом случае ошибку, преобразованную 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');
}
Обратите внимание, что для проверки возвращаемого значения вы должны использовать оператор подавления ошибок при вызове функции (@
перед именем функции). Таким образом, исключение не будет выбрано, и выполнение будет продолжено. Затем вы должны установить ожидаемое исключение, как обычно, для проверки ошибки.
То, что вы не можете сделать, это проверить несколько исключений в unit test.
Ответ 5
Этот ответ немного опоздал на вечеринку, но в любом случае:
Вы можете использовать Netsilik/BaseTestCase (лицензия MIT) для непосредственного тестирования на инициируемые уведомления/предупреждения, не игнорируя их и не преобразуя их в исключения. Поскольку уведомления/предупреждения не преобразуются в исключение, выполнение не останавливается.
composer require netsilik/base-test-case
Тестирование для E_USER_NOTICE
:
<?php
namespace Tests;
class MyTestCase extends \Netsilik\Testing\BaseTestCase
{
public function test_whenNoticeTriggered_weCanTestForIt()
{
$foo = new Foo();
$foo->bar();
self::assertErrorTriggered(E_USER_NOTICE, 'The notice message');
}
}
Надеюсь, это поможет кому-то в будущем.