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

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

Ответ 1

<?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);
    }
}

waitException() документация PHPUnit

Статья автора PHPUnit содержит подробное объяснение лучших практик тестирования исключений.

Ответ 2

Вы также можете использовать аннотацию docblock:

class ExceptionTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException InvalidArgumentException
     */
    public function testException()
    {
        ...
    }
}

Для PHP 5.5+ (особенно с расширением имен) я предпочитаю использовать ::class

Ответ 3

Если вы используете 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.

Ответ 4

Код ниже проверяет сообщение об исключении и код исключения.

Важно: Он не удастся, если ожидаемое исключение не будет также выбрано.

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());
}

Ответ 5

Вы можете использовать расширение assertException, чтобы утверждать более одного исключения в течение одного теста.

Вставьте метод в свою тестовую базу и используйте:

public function testSomething()
{
    $test = function() {
        // some code that has to throw an exception
    };
    $this->assertException( $test, 'InvalidArgumentException', 100, 'expected message' );
}

Я также сделал черту для любителей красивого кода.

Ответ 6

Альтернативный способ может быть следующим:

$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Expected Exception Message');

Убедитесь, что ваш тестовый класс \PHPUnit_Framework_TestCase.

Ответ 7

public function testException() {
    try {
        $this->methodThatThrowsException();
        $this->fail("Expected Exception has not been raised.");
    } catch (Exception $ex) {
        $this->assertEquals($ex->getMessage(), "Exception message");
    }

}

Ответ 8

Комплексное решение

PHPUnit в настоящее время "лучшие практики" для тестирования исключений кажутся.. тусклыми (документы).

Так как я хотел больше, чем текущая реализация expectException, я использовал эту черту в своих тестовых примерах. Это всего лишь ~ 50 строк кода.

  • Поддерживает несколько исключений для теста
  • Поддерживает утверждения, вызываемые после возникновения исключения
  • Надежные и понятные примеры использования
  • Стандартный синтаксис assert
  • Поддерживает утверждения не только для сообщения, кода и класса
  • Поддерживает обратное утверждение, assertNotThrows
  • Поддерживает ошибки PHP 7 Throwable

Библиотека

Я опубликовал черту AssertThrows для Github и packagist, чтобы ее можно было установить с помощью composer.

Простой пример

Просто чтобы проиллюстрировать дух синтаксиса:

<?php

// Using simple callback
$this->assertThrows(MyException::class, [$obj, 'doSomethingBad']);

// Using anonymous function
$this->assertThrows(MyException::class, function() use ($obj) {
    $obj->doSomethingBad();
});

Довольно опрятно?


Пример полного использования

Ниже приведен более подробный пример использования:

<?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();
        });
    }
}

?>

Ответ 9

Метод 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');
    });
}

Ответ 10

Здесь все утверждения исключения, которые вы можете сделать. Обратите внимание, что все они являются необязательными.

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!');
    }
}

Документацию можно найти здесь.

Ответ 11

/**
 * @expectedException Exception
 * @expectedExceptionMessage Amount has to be bigger then 0!
 */
public function testDepositNegative()
{
    $this->account->deposit(-7);
}

Будьте очень осторожны в отношении "/**", обратите внимание на двойное "*". Написание только "**" (asterix) приведет к отказу вашего кода. Также убедитесь, что вы используете последнюю версию phpUnit. В некоторых более ранних версиях phpunit @expectedException Exception не поддерживается. У меня было 4.0, и это не сработало для меня, мне пришлось обновить до 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer, чтобы обновить композитор.

Ответ 12

Для PHPUnit 5.7.27 и PHP 5.6 и для тестирования нескольких исключений в одном тесте важно было принудительно выполнить тестирование исключений. Использование только обработки исключений для утверждения экземпляра Exception пропустит тестирование ситуации, если исключение не происходит.

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());

}

Ответ 13

function yourfunction($a,$z){
   if($a<$z){ throw new <YOUR_EXCEPTION>; }
}

вот тест

class FunctionTest extends \PHPUnit_Framework_TestCase{

   public function testException(){

      $this->setExpectedException(<YOUR_EXCEPTION>::class);
      yourfunction(1,2);//add vars that cause the exception 

   }

}

Ответ 14

PhpUnit - удивительная библиотека, но этот конкретный момент немного расстраивает. Вот почему мы можем использовать открытую библиотеку turbotesting-php, которая имеет очень удобный метод утверждения, чтобы помочь нам тестировать исключения. Он находится здесь:

https://github.com/edertone/TurboTesting/blob/master/TurboTesting-Php/src/main/php/utils/AssertUtils.php

И чтобы использовать его, мы просто сделаем следующее:

AssertUtils::throwsException(function(){

    // Some code that must throw an exception here

}, '/expected error message/');

Если код, который мы вводим внутри анонимной функции, не выдает исключение, будет выдано исключение.

Если код, который мы вводим внутри анонимной функции, генерирует исключение, но его сообщение не соответствует ожидаемому регулярному выражению, то также будет выдано исключение.