DEV Community

Philipp Scheit
Philipp Scheit

Posted on

Trick for testing exceptions in PHPUnit

When you're using PHPUnit to test for an exception you can use $this->expectException(EvatrInvalidVatNumberException::class); for setting up your test to listen to the exception. If the expected exception is not thrown your test will fail and PHPUnit will report the issue.

When you only need to assert the message (or a substring) you would use $this->expectExceptionMessage().

In most test cases this works very well. But sometimes you might want to assert some more aspects of the exception. For example if it contains some debug infos.

Of course you could try to use $this->expectExceptionObject() but this is only a shortcut for expecting the class, message and code. It won't compare properties on the exception for example (!).

You could assert that the exception is thrown when you write the whole test by hand like this:

public function testAnExceptionIsThrown(): void 
{
  try {
    $this->start();
  } catch (Domain\Exception $e) {
    $this->assertEquals('some debug info', $e->debugInfo);
    return;
  }

  $this->fail('Expected Exception is not thrown');
}
Enter fullscreen mode Exit fullscreen mode

This works. But its not very nice. The fail now does not provide any information anymore which exception is thrown instead. Therefore does not convey why the tests fails. And the return in the catch is hard to read and easy to miss.

Looking closer at it, there is another issue. The exception count is wrong: assertContains when the test passes, but actually you have two assertions

  1. The correct exception is thrown
  2. The exception contains the debug info

A Better Approach

public function testAnExceptionIsThrownWithDebugInfo(): void
{
  $this->expectException(Domain\Exception::class);

  try {
    $this->start();
  } catch (Domain\Exception $e) {
    $this->assertEquals('some debug info', $e->debugInfo);
    throw $e;
  }
}
Enter fullscreen mode Exit fullscreen mode

Testing the exception like this has some advantages:

  • Assertion count is now correct
  • Your test will still be reporting correctly if the exception is changed
  • It's more readable
  • When you miss to add the throw the test will still fail
  • The failing test (wrong exception thrown) will report nicely which exception is caught instead and skip the assertEquals

Originally published on ps-webforge.com on March 3rd, 2013

Top comments (1)

Collapse
 
xwero profile image
david duymelinck

If you re-upload an article, I suggest you add updated information. expectExceptionMessage is available since 2015. Which reduces the test code to

public function testAnExceptionIsThrownWithDebugInfo(): void
{
  $this->expectException(Domain\Exception::class);

  $this->expectExceptionMessage('some debug info');

   $this->start();
}

Enter fullscreen mode Exit fullscreen mode