Как проверить несколько исключений с помощью PHPUnit?
При тестировании исключений с помощью PHPUnit, каков наилучший способ потребовать, чтобы каждый оператор или утверждение должны вызывать исключение для прохождения теста?
В основном я хочу сделать что-то вроде этого:
public function testExceptions()
{
$this->setExpectedException('Exception');
foo(-1); //throws exception
foo(1); //does not throw exception
}
//Test will fail because foo(1) did not throw an exception
Я придумал следующее, которое выполняет эту работу, но довольно уродливое ИМО.
public function testExceptions()
{
try {
foo(-1);
} catch (Exception $e) {
$hit = true;
}
if (!isset($hit))
$this->fail('No exception thrown');
unset($hit);
try {
foo(1);
} catch (Exception $e) {
$hit = true;
}
if (!isset($hit))
$this->fail('No exception thrown');
unset($hit);
}
Ответы
Ответ 1
В качестве исключений такие большие события в потоке программы, тестирование нескольких из них в одном тесте проблематично.
Самое простое - просто разбить его на два теста - первое требует исключения, чтобы пройти, второе просто запускается и не сможет его выбросить. Вы могли бы добавить некоторые другие тесты во втором случае, если хотите (возможно, подтверждение возвращаемого значения), но я был бы склонен удостовериться, что он по-прежнему имеет только одну существенную вещь, согласно ее названию.
/**
* @expectedException Exception
*/
public function testBadFooThrowsException()
{
// optional, can also do it from the '@expectedException x'
//$this->setExpectedException('Exception');
foo(-1); //throws exception -- good.
}
public function testFooDoesNotThrowException()
{
foo(1); //does not throw exception
}
Ответ 2
Я думаю, что это очень распространенная ситуация в модульном тестировании. Подход, который я использую в этом случае, использует phpunit dataProviders. Все работает так, как ожидалось, и тестовый код становится более понятным и лаконичным.
class MyTest extends PHPUnit_Framework_TestCase
{
public function badValues()
{
return array(
array(-1),
array(1)
);
}
/**
* @dataProvider badValues
* @expectedException Exception
*/
public function testFoo($badValue)
{
foo($badValue);
}
}
Ответ 3
Чуть более чистый код (но я все же предлагаю расщепить ваши тесты:
try {
foo(-1);
$this->fail('No exception thrown');
} catch (Exception $e) {}
Ответ 4
Это не имеет смысла для меня.
Я думаю, вы пытаетесь протестировать несколько отдельных вещей с помощью одного тестового примера, который является плохой практикой.
Когда foo()
выбрасывает ожидаемое исключение, тестовый пример успешный и bar()
не будет выполняться.
Просто создайте два отдельных тестовых примера, которые намного меньше кода, чем то, что вы произвели во втором листинге.
Или объясните, почему было бы целесообразно запустить bar()
, после того как foo()
завершился с ошибкой с исключением, когда он тоже выдаст исключение.
Ответ 5
Развернувшись на @dave1010, ответьте, вот как я решил эту проблему. Это позволяет сохранить все эти "утверждения" аккуратно и аккуратно в течение одного теста. Вы просто определяете массив переменных, которые должны пропустить тест, а затем прокручивать каждый из них и видеть, создано ли исключение. Если какой-либо сбой (без исключения), тест не выполняется, в противном случае тест проходит.
<?php
public function testSetInvalidVariableType()
{
$invalid_vars = array(
'', // Strings
array(), // Arrays
true, // Booleans
1, // Integers
new \StdClass // Objects
);
foreach ($invalid_vars as $var) {
try {
$object->method($var);
$this->fail('No exception thrown for variable type "' . gettype($var) . '".');
} catch (\Exception $expected) {
}
}
}