An(other) Alternative Approach To Testing Exceptions With PHPUnit

The PHPUnit manual suggests the following, alternative approach to testing exceptions.  This is an alternative to using the expectedException annotation, or calling setExpectedException() at the start of your test.  You need to use this pattern, or one similar, to test the error message / code returned when a method throws the same exception-type in response to different error conditions – a method may throw InvalidArgumentExceptions in response to different problems with each of its arguments, for example.

<?php class ExceptionTest extends PHPUnit_Framework_TestCase { public function testException() { try { // ... Code that is expected to raise an exception ... } catch (InvalidArgumentException $expected) { return; } $this->fail('An expected exception has not been raised.'); } } ?>

Quickly add some code for testing the error message, and you get the following – adapted from an Ember test.

<?php namespace ember\test; class ElementTest extends \PHPUnit_Framework_TestCase { /** * @dataProvider invalidTextContent */ public function testWithtextThrowsAnExceptionIfTheSpecifiedValueIsInvalid($p_invalidTextContent) { $oElement = new \ember\Element('p'); try { $oElement->withText($p_invalidTextContent); } catch (\InvalidArgumentException $e) { if ($e->getMessage() == 'The text content is invalid') { return; } } $this->fail(); } public static function invalidTextContent() { return array( array(123), array(array()), array(new \stdClass), ); } } ?>

The problem with this is the test will pass with barely a whisper, or fail miserably with little useful feedback, simply because there is no assertion.  Since we’re actually looking for a specific error message / code, we can write the following, which not only makes more sense, but will also give better feedback and is more readable.

<?php namespace ember\test; class ElementTest extends \PHPUnit_Framework_TestCase { /** * @dataProvider invalidTextContent */ public function testWithtextThrowsAnExceptionIfTheSpecifiedValueIsInvalid($p_invalidTextContent) { $oElement = new \ember\Element('p'); try { $oElement->withText($p_invalidTextContent); } catch (\InvalidArgumentException $e) { return $this->assertEquals('The text content is invalid', $e->getMessage()); } $this->fail(); } public static function invalidTextContent() { return array( array(123), array(array()), array(new \stdClass), ); } } ?>