web-dev-qa-db-fra.com

PHPUnit affirme-t-il qu'une exception a été levée?

Est-ce que quelqu'un sait s'il existe une assert ou quelque chose comme ça qui peut vérifier si une exception a été générée dans le code testé?

248
Felipe Almeida
<?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 () documentation PHPUnit

PHPUnit author article fournit des explications détaillées sur les meilleures pratiques en matière de test des exceptions.

422
Frank Farmer

Vous pouvez également utiliser une annotation docblock :

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

Pour PHP 5.5+ (en particulier avec du code namespaced), je préfère maintenant utiliser ::class

108
David Harkness

Si vous exécutez PHP 5.5+, vous pouvez utiliser ::class resolution pour obtenir le nom de la classe avec expectExceptionsetExpectedException . Cela offre plusieurs avantages:

  • Le nom sera qualifié complet avec son espace de noms (le cas échéant).
  • Il se résout en un string afin qu'il fonctionne avec toutes les versions de PHPUnit.
  • Vous obtenez la complétion de code dans votre IDE.
  • Le compilateur PHP émettra une erreur si vous tapez mal le nom de la classe.

Exemple:

namespace \My\Cool\Package;

class AuthTest extends \PHPUnit_Framework_TestCase
{
    public function testLoginFailsForWrongPassword()
    {
        $this->expectException(WrongPasswordException::class);
        Auth::login('Bob', 'wrong');
    }
}

PHP compile

WrongPasswordException::class

dans

"\My\Cool\Package\WrongPasswordException"

sans PHPUnit le plus sage.

_/Note: PHPUnit 5.2 introduitexpectException en remplacement de setExpectedException.

31
David Harkness

Le code ci-dessous testera le message d'exception et le code d'exception. 

Important: Il échouera si une exception attendue n'est pas levée également.

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

Vous pouvez utiliser extension assertException pour affirmer plusieurs exceptions lors de l'exécution d'un test.

Insérer une méthode dans votre TestCase et utiliser:

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

J'ai aussi créé un trait pour les amoureux du code de Nice.

23
hejdav

Une autre manière peut être la suivante:

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

Assurez-vous que l'étendue de votre classe de test est \PHPUnit_Framework_TestCase.

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

}
9
ab_wanyama

Solution complète

Les " meilleures pratiques " actuelles de PHPUnit pour le test des exceptions semblent .. terne ( docs ).

Étant donné que je suis tout à fait en désaccord avec la mise en œuvre actuelle de expectException, j'ai créé un trait à utiliser dans mes cas de test. Ce n'est que 50 lignes .

  • Prend en charge plusieurs exceptions par test
  • Prend en charge les assertions appelées après le lancement de l'exception
  • Des exemples d'utilisation robustes et clairs
  • Syntaxe standard assert
  • Prend en charge les assertions pour plus que simplement message, code et classe
  • Prend en charge l'assertion inverse, assertNotThrows

Bibliothèque

J'ai publié le AssertThrows trait en Github et packagist afin qu'il puisse être installé avec composer.

Exemple simple

Juste pour illustrer l'esprit derrière la syntaxe:

<?php

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

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

Génial?


Exemple d'utilisation complète

Veuillez voir ci-dessous pour un exemple d'utilisation plus complet:

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

?>
7
jchook

La méthode PHPUnit expectException est très gênante car elle ne permet de tester qu'une seule exception par méthode.

J'ai créé cette fonction d'assistance pour affirmer qu'une fonction lève une exception:

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

Ajoutez-le à votre classe de test et appelez de cette façon:

public function testSomething() {
    $this->assertException(\PDOException::class, function() {
        new \PDO('bad:param');
    });
    $this->assertException(\PDOException::class, function() {
        new \PDO('foo:bar');
    });
}
5
Finesse

Voici toutes les assertions d'exception que vous pouvez faire. Notez qu'ils sont tous optionnels.

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

La documentation peut être trouvée ici .

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

Faites très attention à "/**", remarquez le double "*". Écrire seulement "**" (astérix) fera échouer votre code . Assurez-vous également que vous utilisez la dernière version de phpUnit. Dans certaines versions antérieures de phpunit, @expectedException Exception n'est pas pris en charge. J'avais 4.0 et cela ne fonctionnait pas pour moi, je devais mettre à jour vers 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer pour mettre à jour avec composer.

2
Catalin Cislariu

Pour PHPUnit 5.7.27 et PHP 5.6 et pour tester plusieurs exceptions dans un test, il était important de forcer le test des exceptions. L'utilisation de la gestion des exceptions seule pour affirmer l'instance d'Exception évite de tester la situation si aucune exception ne se produit.

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

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

voici le test

class FunctionTest extends \PHPUnit_Framework_TestCase{

   public function testException(){

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

   }

}
0
sami klah