Как в PHPUnit проверить, что метод выбросил исключение?
Использование встроенных методов PHPUnit для проверки исключений
Да, в PHPUnit есть специальные методы для проверки исключений. Ваш текущий подход с try...catch работает, но он громоздкий и не использует возможности фреймворка. Вот три основных способа, которые предоставляет PHPUnit:
1. Метод expectException() (рекомендуемый)
Это самый популярный и читаемый способ. Вы просто вызываете $this->expectException() до того, как выполните код, который должен выбросить исключение:
public function testDivisionByZero() {
$calculator = new Calculator();
// Говорим PHPUnit, что ожидаем исключение
$this->expectException(\InvalidArgumentException::class);
// Выполняем код, который должен выбросить исключение
$calculator->divide(10, 0);
}
Если исключение не будет выброшено, тест упадёт с ошибкой "Failed asserting that exception of type "InvalidArgumentException" is thrown". Если исключение другого типа — тоже упадёт.
2. Проверка сообщения и кода исключения
Часто нужно проверить не только тип исключения, но и его сообщение или код. Для этого есть дополнительные методы:
public function testDivisionByZeroWithMessage() {
$calculator = new Calculator();
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Division by zero.'); // точное совпадение
// или $this->expectExceptionMessageMatches('/division/i'); // по регулярке
$calculator->divide(10, 0);
}
Также можно проверить код исключения с помощью expectExceptionCode():
$this->expectExceptionCode(100); // если у исключения есть код
3. Атрибут #[ExpectException] (PHPUnit 10+)
В новых версиях PHPUnit (начиная с 10) появилась возможность использовать атрибуты. Это более декларативный стиль:
use PHPUnit\Framework\Attributes\ExpectException;
class CalculatorTest extends TestCase {
#[ExpectException(\InvalidArgumentException::class)]
public function testDivisionByZero() {
$calculator = new Calculator();
$calculator->divide(10, 0);
}
}
Можно комбинировать с другими атрибутами:
#[ExpectException(\InvalidArgumentException::class)]
#[ExpectExceptionMessage('Division by zero.')]
public function testDivisionByZero() {
// ...
}
Важные моменты
- Порядок важен: методы
expectException*должны вызываться до того, как будет выполнен код, выбрасывающий исключение. - Одно исключение на тест: старайтесь проверять только одно исключение в одном тесте. Если нужно проверить несколько сценариев — пишите отдельные тесты.
- Не ловите исключение вручную: если вы напишете
try...catchвнутри теста сexpectException, PHPUnit может не заметить, что исключение было выброшено.
Используя эти методы, ваш тест станет лаконичным, читаемым и будет следовать best practices PHPUnit.
<<Альтернативный подход: Data Provider для множественных сценариев
Если у вас есть несколько случаев, когда метод должен выбрасывать исключение, удобно использовать @dataProvider вместе с expectException(). Это позволяет избежать дублирования кода:
class CalculatorTest extends TestCase {
/**
* @dataProvider divisionByZeroProvider
*/
public function testDivisionByZero($a, $b) {
$calculator = new Calculator();
$this->expectException(\InvalidArgumentException::class);
$calculator->divide($a, $b);
}
public function divisionByZeroProvider(): array {
return [
[10, 0],
[0, 0],
[-5, 0],
];
}
}
Этот подход особенно полезен, когда нужно проверить граничные значения или разные типы некорректных входных данных.
Также для старых версий PHPUnit (до 6.0) можно использовать аннотацию @expectedException в docblock, но это устаревший синтаксис, и его не рекомендуется применять в новых проектах:
/**
* @expectedException \InvalidArgumentException
*/
public function testDivisionByZero() {
// ...
}
Советую переходить на современные методы — expectException() или атрибуты, так как они лучше поддерживаются IDE и статическими анализаторами.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги