[php] PHPUnit assert that an exception was thrown?

Does anyone know whether there is an assert or something like that which can test whether an exception was thrown in the code being tested?

This question is related to php exception phpunit assert

The answer is


An alternative way can be the following:

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

Please ensure that your test class extents \PHPUnit_Framework_TestCase.


PhpUnit is an amazing library, but this specific point is a bit frustrating. This is why we can use the turbotesting-php opensource library which has a very convenient assertion method to help us testing exceptions. It is found here:

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

And to use it, we would simply do the following:

AssertUtils::throwsException(function(){

    // Some code that must throw an exception here

}, '/expected error message/');

If the code we type inside the anonymous function does not throw an exception, an exception will be thrown.

If the code we type inside the anonymous function throws an exception, but its message does not match the expected regexp, an exception will also be thrown.


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

}

Here's all the exception assertions you can do. Note that all of them are optional.

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

Documentation can be found here.


You can also use a docblock annotation until PHPUnit 9 is released:

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

For PHP 5.5+ (especially with namespaced code), I now prefer using ::class


Code below will test exception message and exception code.

Important: It will fail if expected exception not thrown too.

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

The PHPUnit expectException method is very inconvenient because it allows to test only one exception per a test method.

I've made this helper function to assert that some function throws an 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');
}

Add it to your test class and call this way:

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

For PHPUnit 5.7.27 and PHP 5.6 and to test multiple exceptions in one test, it was important to force the exception testing. Using exception handling alone to assert the instance of Exception will skip testing the situation if no exception occurs.

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

}

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

here is the test

class FunctionTest extends \PHPUnit_Framework_TestCase{

   public function testException(){

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

   }

}

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

Be very carefull about "/**", notice the double "*". Writing only "**"(asterix) will fail your code. Also make sure your using last version of phpUnit. In some earlier versions of phpunit @expectedException Exception is not supported. I had 4.0 and it didn't work for me, I had to update to 5.5 https://coderwall.com/p/mklvdw/install-phpunit-with-composer to update with composer.


You can use assertException extension to assert more than one exception during one test execution.

Insert method into your TestCase and use:

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

I also made a trait for lovers of nice code..


If you're running on PHP 5.5+, you can use ::class resolution to obtain the name of the class with expectException/setExpectedException. This provides several benefits:

  • The name will be fully-qualified with its namespace (if any).
  • It resolves to a string so it will work with any version of PHPUnit.
  • You get code-completion in your IDE.
  • The PHP compiler will emit an error if you mistype the class name.

Example:

namespace \My\Cool\Package;

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

PHP compiles

WrongPasswordException::class

into

"\My\Cool\Package\WrongPasswordException"

without PHPUnit being the wiser.

Note: PHPUnit 5.2 introduced expectException as a replacement for setExpectedException.


Comprehensive Solution

PHPUnit's current "best practices" for exception testing seem.. lackluster (docs).

Since I wanted more than the current expectException implementation, I made a trait to use on my test cases. It's only ~50 lines of code.

  • Supports multiple exceptions per test
  • Supports assertions called after the exception is thrown
  • Robust and clear usage examples
  • Standard assert syntax
  • Supports assertions for more than just message, code, and class
  • Supports inverse assertion, assertNotThrows
  • Supports PHP 7 Throwable errors

Library

I published the AssertThrows trait to Github and packagist so it can be installed with composer.

Simple Example

Just to illustrate the spirit behind the syntax:

<?php

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

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

Pretty neat?


Full Usage Example

Please see below for a more comprehensive usage example:

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

?>

Examples related to php

I am receiving warning in Facebook Application using PHP SDK Pass PDO prepared statement to variables Parse error: syntax error, unexpected [ Preg_match backtrack error Removing "http://" from a string How do I hide the PHP explode delimiter from submitted form results? Problems with installation of Google App Engine SDK for php in OS X Laravel 4 with Sentry 2 add user to a group on Registration php & mysql query not echoing in html with tags? How do I show a message in the foreach loop?

Examples related to exception

Connection Java-MySql : Public Key Retrieval is not allowed How to print an exception in Python 3? ASP.NET Core Web API exception handling Catching FULL exception message How to get exception message in Python properly What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean? what does Error "Thread 1:EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)" mean? Argument Exception "Item with Same Key has already been added" The given key was not present in the dictionary. Which key? sql try/catch rollback/commit - preventing erroneous commit after rollback

Examples related to phpunit

How do I get HTTP Request body content in Laravel? How to run single test method with phpunit? How to output in CLI during execution of PHP Unit tests? PHPUnit assert that an exception was thrown? Best practices to test protected methods with PHPUnit

Examples related to assert

What is “assert” in JavaScript? How to handle AssertionError in Python and find out which line or statement it occurred on? How can I check if some text exist or not in the page using Selenium? PHPUnit assert that an exception was thrown? What is the use of "assert"? How do I check (at runtime) if one class is a subclass of another? Java/ JUnit - AssertTrue vs AssertFalse What does the "assert" keyword do? Proper way to assert type of variable in Python How to check if an object is a list or tuple (but not string)?