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 для утверждения более одного исключения во время одного выполнения теста.
вставьте метод в свой TestCase и используйте:
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');
пожалуйста, убедитесь, что ваш тестовый класс extents \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
реализация, я сделал черту, чтобы использовать на моих тестовых случаях.
библиотека
на 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());
}