Permalink
Browse files

New assertion to test if a given exception is thrown.

Note that this is a minimal implementation, for demonstration purposes, with
just enough code to make it work in PHPUnit.
For a longer discussion see:
http://lars-tesmer.com/blog/2011/08/29/phpunit-better-syntax-for-expecting-exceptions/

Example usage:
$someClass = new SomeClass();
$this->assertThrowsException('InvalidArgumentException', function () use($someClass) {
		$someClass->someMethod();
	}
);
  • Loading branch information...
1 parent 7f7ad8b commit dd5c7bd71d6eb8d4b58ce79b5ae069fbb0734354 Lars Tesmer committed Aug 29, 2011
Showing with 84 additions and 0 deletions.
  1. +6 −0 PHPUnit/Framework/Assert.php
  2. +56 −0 PHPUnit/Framework/Constraint/ExceptionThrown.php
  3. +22 −0 Tests/Framework/AssertTest.php
@@ -2071,6 +2071,12 @@ public static function assertNotTag($matcher, $actual, $message = '', $isHtml =
self::assertFalse($matched, $message);
}
+ public static function assertThrowsException($exceptionName, $code)
+ {
+ $constraint = new PHPUnit_Framework_Constraint_ExceptionThrown($exceptionName);
+ self::assertThat($code, $constraint);
+ }
+
/**
* Evaluates a PHPUnit_Framework_Constraint matcher object.
*
@@ -0,0 +1,56 @@
+<?php
+class PHPUnit_Framework_Constraint_ExceptionThrown extends PHPUnit_Framework_Constraint {
+
+ private $_exceptionName;
+ private $_exceptionThrown = null;
+
+ public function __construct($exceptionName)
+ {
+ $this->_exceptionName = $exceptionName;
+ }
+
+ /**
+ * Evaluates the constraint for parameter $other. Returns TRUE if the
+ * constraint is met, FALSE otherwise.
+ *
+ * @param mixed $other Value or object to evaluate.
+ * @return bool
+ */
+ public function evaluate($code)
+ {
+ try {
+ $code();
+ } catch (\Exception $e) {
+ $this->_exceptionThrown = $e;
+ }
+
+ if (!$this->_exceptionThrown) {
+ return false;
+ }
+
+ if (!$this->_exceptionThrown instanceof \Exception) {
+ return false;
+ }
+
+ if (!$this->_exceptionThrown instanceof $this->_exceptionName) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public function customFailureDescription($other, $description, $not)
+ {
+ if(!$this->_exceptionThrown || !$this->_exceptionThrown instanceof \Exception) {
+ $failureDescription = sprintf("Expected exception <%s> but no exception was thrown.", $this->_exceptionName);
+ } else {
+ $failureDescription = sprintf("Expected exception: <%s>\nCaught Exception: <%s>.", $this->_exceptionName, get_class($this->_exceptionThrown));
+ }
+ return $failureDescription;
+ }
+
+ public function toString()
+ {
+ }
+
+}
@@ -4520,4 +4520,26 @@ public function testMarkTestSkipped()
$this->fail();
}
+
+ /**
+ * @covers PHPUnit_Framework_Assert::assertThrowsException
+ */
+ public function testAssertThrowsException()
+ {
+ $code = function() {
+ throw new InvalidArgumentException('test');
+ };
+
+ $this->assertThrowsException('InvalidArgumentException', $code);
+
+ try {
+ $this->assertThrowsException('MyMadeUpException', $code);
+ }
+
+ catch (PHPUnit_Framework_AssertionFailedError $e) {
+ return;
+ }
+
+ $this->fail();
+ }
}

2 comments on commit dd5c7bd

You could use __staticCall() to move the exception name from a string parameter into the method name. This might make the API a little nicer for the built-in (non-package) exceptions:

self::assertThrowsInvalidArgumentException(function () use($someClass) {
    $someClass->someMethod();
});
Owner

kiltec replied Jul 13, 2012

Yes, that's a nice idea, will fiddle around with it, to see if it's really better, thanks! :)

Please sign in to comment.