PHPUnit утверждают, что исключение было брошено?


кто-нибудь знает, есть ли assert или что-то вроде того, что может проверить, было ли вызвано исключение в тестируемом коде?

12 229

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\MyExceptio‌​n');
        $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());

}