PHPUnit утверждают, что исключение было брошено?
кто-нибудь знает, есть ли assert
или что-то вроде того, что может проверить, было ли вызвано исключение в тестируемом коде?
12 ответов:
<?php require_once 'PHPUnit/Framework.php'; class ExceptionTest extends PHPUnit_Framework_TestCase { public function testException() { $this->expectException(InvalidArgumentException::class); // or for PHPUnit < 5.2 // $this->setExpectedException(InvalidArgumentException::class); //...and then add your test code that generates the exception exampleMethod($anInvalidArgument); } }
expectException () PHPUnit documentation
PHPUnit автор статьи предоставляет подробное объяснение по тестированию исключений лучших практик.
вы также можете использовать аннотация docblock:
class ExceptionTest extends PHPUnit_Framework_TestCase { /** * @expectedException InvalidArgumentException */ public function testException() { ... } }
для PHP 5.5+ (особенно с пространством имен кода), теперь я предпочитаю использовать
::class
если вы работаете на PHP 5.5+, вы можете использовать
::class
разрешение чтобы получить имя класса с помощьюexpectException
/setExpectedException
. Это дает несколько преимуществ:
- имя будет полностью определено с его пространством имен (если таковое имеется).
- он является
string
так что он будет работать с любой версией PHPUnit.- вы получаете код-завершение в вашей IDE.
- компилятор PHP выдаст ошибку, если вы введите имя класса с ошибкой.
пример:
namespace \My\Cool\Package; class AuthTest extends \PHPUnit_Framework_TestCase { public function testLoginFailsForWrongPassword() { $this->expectException(WrongPasswordException::class); Auth::login('Bob', 'wrong'); } }
PHP компилирует
WrongPasswordException::class
на
"\My\Cool\Package\WrongPasswordException"
без PHPUnit быть мудрее.
Примечание:PHPUnit 5.2 представил
expectException
как замена дляsetExpectedException
.
код ниже будет тестировать сообщение об исключении и код исключения.
важно: это не сработает, если ожидаемое исключение тоже не будет брошено.
try{ $test->methodWhichWillThrowException();//if this method not throw exception it must be fail too. $this->fail("Expected exception 1162011 not thrown"); }catch(MySpecificException $e){ //Not catching a generic Exception or the fail function is also catched $this->assertEquals(1162011, $e->getCode()); $this->assertEquals("Exception Message", $e->getMessage()); }
можно использовать расширение assertException для утверждения более одного исключения во время выполнения одного теста.
вставьте метод в свой тестовый случай и используйте:
public function testSomething() { $test = function() { // some code that has to throw an exception }; $this->assertException( $test, 'InvalidArgumentException', 100, 'expected message' ); }
я тоже сделал признак для любителей хорошего кода..
public function testException() { try { $this->methodThatThrowsException(); $this->fail("Expected Exception has not been raised."); } catch (Exception $ex) { $this->assertEquals($ex->getMessage(), "Exception message"); } }
альтернативный способ может быть следующим:
$this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Expected Exception Message');
пожалуйста, убедитесь, что ваш тестовый класс экстенты
\PHPUnit_Framework_TestCase
.
вот все утверждения исключения, которые вы можете сделать. Обратите внимание, что все они дополнительно.
class ExceptionTest extends PHPUnit_Framework_TestCase { public function testException() { // make your exception assertions $this->expectException(InvalidArgumentException::class); // if you use namespaces: // $this->expectException('\Namespace\MyException'); $this->expectExceptionMessage('message'); $this->expectExceptionMessageRegExp('/essage$/'); $this->expectExceptionCode(123); // code that throws an exception throw new InvalidArgumentException('message', 123); } public function testAnotherException() { // repeat as needed $this->expectException(Exception::class); throw new Exception('Oh no!'); } }
документация может быть найдена здесь.
по PHPUnit это "лучшие практики" для проверки исключений.. тусклый.
- не поддерживает несколько исключений для каждого теста или утверждений, вызываемых после возникновения исключения
- в документации отсутствуют надежные или четкие примеры
- нестандартный и потенциально запутанный синтаксис ("expect" vs "assert")
- поддерживает только утверждения для сообщения, кода и класса
- нет обратного, например "expectNoException"
открыл выпуск Github для PHPUnit и был немедленно уволен сопровождающим.
так как я категорически не согласен с текущим
expectException
реализация, я сделал черту, чтобы использовать на моих тестовых случаях.библиотека
The
AssertThrows
черта публикуется в Github и packagist поэтому он может быть установлен с композитором.простой Пример
просто, чтобы проиллюстрировать дух синтаксис:
<?php // Within your test case... $this->assertThrows(MyException::class, function() use ($obj) { $obj->doSomethingBad(); });
довольно аккуратный?
Полный Пример Использования
вот фактический класс TestCase, который показывает более полный пример использования:
<?php declare(strict_types=1); use Jchook\AssertThrows\AssertThrows; use PHPUnit\Framework\TestCase; // These are just for illustration use MyNamespace\MyException; use MyNamespace\MyObject; final class MyTest extends TestCase { use AssertThrows; // <--- adds the assertThrows method public function testMyObject() { $obj = new MyObject(); // Test a basic exception is thrown $this->assertThrows(MyException::class, function() use ($obj) { $obj->doSomethingBad(); }); // Test custom aspects of a custom extension class $this->assertThrows(MyException::class, function() use ($obj) { $obj->doSomethingBad(); }, function($exception) { $this->assertEquals('Expected value', $exception->getCustomThing()); $this->assertEquals(123, $exception->getCode()); } ); // Test that a specific exception is NOT thrown $this->assertNotThrows(MyException::class, function() use ($obj) { $obj->doSomethingGood(); }); } } ?>
PHPUnit
expectException
метод очень неудобен, потому что он позволяет тестировать только одно исключение для каждого метода тестирования.Я сделал эту вспомогательную функцию, чтобы утверждать, что какая-то функция вызывает исключение:
/** * Asserts that the given callback throws the given exception. * * @param string $expectClass The name of the expected exception class * @param callable $callback A callback which should throw the exception */ protected function assertException(string $expectClass, callable $callback) { try { $callback(); } catch (\Throwable $exception) { $this->assertInstanceOf($expectClass, $exception, 'An invalid exception was thrown'); return; } $this->fail('No exception was thrown'); }
добавьте его в свой тестовый класс и вызовите следующим образом:
public function testSomething() { $this->assertException(\PDOException::class, function() { new \PDO('bad:param'); }); $this->assertException(\PDOException::class, function() { new \PDO('foo:bar'); }); }
/** * @expectedException Exception * @expectedExceptionMessage Amount has to be bigger then 0! */ public function testDepositNegative() { $this->account->deposit(-7); }
будьте очень осторожны
"/**"
обратите внимание на двойное "*". Написание только "* * "(asterix) приведет к сбою кода. Также убедитесь, что вы используете последнюю версию phpUnit. В некоторых более ранних версиях phpunit @expectedException исключение не поддерживается. У меня был 4.0, и это не сработало для меня, мне пришлось обновить до 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer для обновления с помощью composer.
для PHPUnit 5.7.27 и PHP 5.6 и для тестирования нескольких исключений в одном тесте было важно принудительно выполнить тестирование исключений. Использование только обработки исключений для утверждения экземпляра исключения позволит пропустить тестирование ситуации, если исключение не происходит.
public function testSomeFunction() { $e=null; $targetClassObj= new TargetClass(); try { $targetClassObj->doSomething(); } catch ( \Exception $e ) { } $this->assertInstanceOf(\Exception::class,$e); $this->assertEquals('Some message',$e->getMessage()); $e=null; try { $targetClassObj->doSomethingElse(); } catch ( Exception $e ) { } $this->assertInstanceOf(\Exception::class,$e); $this->assertEquals('Another message',$e->getMessage()); }