diff --git a/README.md b/README.md index 2cad047..45fbb90 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,20 @@ verify($user->getPosts())->notNull(); verify($user->getComments())->isEmpty(); verify($user->getRoles())->notEmpty(); +// throws +verify($callback)->throws(); +verify($callback)->throws(Exception::class); +verify($callback)->throws(Exception::class, 'exception message'); +verify($callback)->throws(new Exception()); +verify($callback)->throws(new Exception('message')); + +// does not throw +verify($callback)->doesNotThrow(); +verify($callback)->doesNotThrow(Exception::class); +verify($callback)->doesNotThrow(Exception::class, 'exception message'); +verify($callback)->doesNotThrow(new Exception()); +verify($callback)->doesNotThrow(new Exception('exception message')); + //Other methods: * stringContainsString * stringNotContainsString diff --git a/src/Codeception/Verify.php b/src/Codeception/Verify.php index 562c49d..2a45d75 100644 --- a/src/Codeception/Verify.php +++ b/src/Codeception/Verify.php @@ -2,6 +2,7 @@ namespace Codeception; use \Codeception\PHPUnit\TestCase as a; +use PHPUnit\Framework\ExpectationFailedException; class Verify { @@ -457,4 +458,67 @@ public function notEqualsWithDelta($expected, $delta) { a::assertNotEqualsWithDelta($expected, $this->actual, $delta, $this->description); } + + /** + * @param \Exception|string|null $throws + * @param string|false $message + * @throws \Throwable + */ + public function throws($throws = null, $message = false) + { + if ($throws instanceof \Exception) { + $message = $throws->getMessage(); + $throws = get_class($throws); + } + + try { + call_user_func($this->actual); + } catch (\Throwable $e) { + if (!$throws) { + return; // it throws + } + + $actualThrows = get_class($e); + $actualMessage = $e->getMessage(); + + a::assertSame($throws, $actualThrows, "exception '$throws' was expected, but '$actualThrows' was thrown"); + + if ($message) { + a::assertSame($message, $actualMessage, "exception message '$message' was expected, but '$actualMessage' was received"); + } + } + + if (!isset($e)) { + throw new ExpectationFailedException("exception '$throws' was not thrown as expected"); + } + } + + public function doesNotThrow($throws = null, $message = false) + { + if ($throws instanceof \Exception) { + $message = $throws->getMessage(); + $throws = get_class($throws); + } + + try { + call_user_func($this->actual); + } catch (\Throwable $e) { + if (!$throws) { + throw new ExpectationFailedException("exception was not expected to be thrown"); + } + + $actualThrows = get_class($e); + $actualMessage = $e->getMessage(); + + if ($throws !== $actualThrows) { + return; + } + + if (!$message) { + throw new ExpectationFailedException("exception '$throws' was not expected to be thrown"); + } elseif ($message === $actualMessage) { + throw new ExpectationFailedException("exception '$throws' with message '$message' was not expected to be thrown"); + } + } + } } diff --git a/tests/VerifyTest.php b/tests/VerifyTest.php index e309c40..e6c5184 100644 --- a/tests/VerifyTest.php +++ b/tests/VerifyTest.php @@ -313,6 +313,56 @@ public function testNotEqualsWithDelta() { verify(1.2)->notEqualsWithDelta(1.0, 0.1); } + + public function testThrows() + { + $func = function () { + throw new Exception('foo'); + }; + + verify($func)->throws(); + verify($func)->throws(Exception::class); + verify($func)->throws(Exception::class, 'foo'); + verify($func)->throws(new Exception()); + verify($func)->throws(new Exception('foo')); + + verify(function () use ($func) { + verify($func)->throws(RuntimeException::class); + })->throws(\PHPUnit\Framework\ExpectationFailedException::class); + + verify(function () { + verify(function () {})->throws(Exception::class); + })->throws(new \PHPUnit\Framework\ExpectationFailedException("exception 'Exception' was not thrown as expected")); + } + + public function testDoesNotThrow() + { + $func = function () { + throw new Exception('foo'); + }; + + verify(function () {})->doesNotThrow(); + verify($func)->doesNotThrow(RuntimeException::class); + verify($func)->doesNotThrow(RuntimeException::class, 'bar'); + verify($func)->doesNotThrow(RuntimeException::class, 'foo'); + verify($func)->doesNotThrow(new RuntimeException()); + verify($func)->doesNotThrow(new RuntimeException('bar')); + verify($func)->doesNotThrow(new RuntimeException('foo')); + verify($func)->doesNotThrow(Exception::class, 'bar'); + verify($func)->doesNotThrow(new Exception('bar')); + + verify(function () use ($func) { + verify($func)->doesNotThrow(); + })->throws(new \PHPUnit\Framework\ExpectationFailedException("exception was not expected to be thrown")); + + verify(function () use ($func) { + verify($func)->doesNotThrow(Exception::class); + })->throws(new \PHPUnit\Framework\ExpectationFailedException("exception 'Exception' was not expected to be thrown")); + + verify(function () use ($func) { + verify($func)->doesNotThrow(Exception::class, 'foo'); + })->throws(new \PHPUnit\Framework\ExpectationFailedException("exception 'Exception' with message 'foo' was not expected to be thrown")); + } }