Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asserts: Add expectThrowable() method #5213

Merged
merged 8 commits into from Oct 18, 2018
25 changes: 17 additions & 8 deletions docs/modules/Asserts.md
Expand Up @@ -314,30 +314,39 @@ Checks that condition is positive.


### expectException

@see expectThrowable
@deprecated Use expectThrowable instead.

* `param` $exception string or \Exception
* `param` $callback


### expectThrowable

Handles and checks exception called inside callback function.
Either exception class name or exception instance should be provided.
Handles and checks throwables (Exceptions/Errors) called inside the callback function.
Either throwable class name or throwable instance should be provided.

```php
<?php
$I->expectException(MyException::class, function() {
$I->expectThrowable(MyThrowable::class, function() {
$this->doSomethingBad();
});

$I->expectException(new MyException(), function() {
$I->expectThrowable(new MyException(), function() {
$this->doSomethingBad();
});
```
If you want to check message or exception code, you can pass them with exception instance:
If you want to check message or throwable code, you can pass them with throwable instance:
```php
<?php
// will check that exception MyException is thrown with "Don't do bad things" message
$I->expectException(new MyException("Don't do bad things"), function() {
// will check that throwable MyError is thrown with "Don't do bad things" message
$I->expectThrowable(new MyError("Don't do bad things"), function() {
$this->doSomethingBad();
});
```

* `param` $exception string or \Exception
* `param` $throwable string or \Throwable
* `param` $callback


Expand Down
112 changes: 86 additions & 26 deletions src/Codeception/Module/Asserts.php
Expand Up @@ -455,40 +455,100 @@ public function fail($message)
*
* @param $exception string or \Exception
* @param $callback
*
* @deprecated Use expectThrowable instead
*/
public function expectException($exception, $callback)
{
$code = null;
$msg = null;
if (is_object($exception)) {
/** @var $exception \Exception **/
$class = get_class($exception);
$msg = $exception->getMessage();
$code = $exception->getCode();
$this->expectThrowable($exception, $callback);
}

/**
* Handles and checks throwables (Exceptions/Errors) called inside the callback function.
* Either throwable class name or throwable instance should be provided.
*
* ```php
* <?php
* $I->expectThrowable(MyThrowable::class, function() {
* $this->doSomethingBad();
* });
*
* $I->expectThrowable(new MyException(), function() {
* $this->doSomethingBad();
* });
* ```
* If you want to check message or throwable code, you can pass them with throwable instance:
* ```php
* <?php
* // will check that throwable MyError is thrown with "Don't do bad things" message
* $I->expectThrowable(new MyError("Don't do bad things"), function() {
* $this->doSomethingBad();
* });
* ```
*
* @param $throwable string or \Throwable
* @param $callback
*/
public function expectThrowable($throwable, $callback)
{
if (is_object($throwable)) {
/** @var $throwable \Throwable */
$class = get_class($throwable);
$msg = $throwable->getMessage();
$code = $throwable->getCode();
} else {
$class = $exception;
$class= $throwable;
$msg = null;
$code = null;
}

try {
$callback();
} catch (\Exception $e) {
if (!$e instanceof $class) {
$this->fail(sprintf("Exception of class $class expected to be thrown, but %s caught", get_class($e)));
}
if (null !== $msg and $e->getMessage() !== $msg) {
$this->fail(sprintf(
"Exception of $class expected to be '$msg', but actual message was '%s'",
$e->getMessage()
));
}
if (null !== $code and $e->getCode() !== $code) {
$this->fail(sprintf(
"Exception of $class expected to have code $code, but actual code was %s",
$e->getCode()
));
}
$this->assertTrue(true); // increment assertion counter
} catch (\Exception $t) {
$this->checkThrowable($t, $class, $msg, $code);

return;
} catch (\Throwable $t) {
$this->checkThrowable($t, $class, $msg, $code);

return;
}
$this->fail("Expected exception of $class to be thrown, but nothing was caught");

$this->fail("Expected throwable of class '$class' to be thrown, but nothing was caught");
}

/**
* Check if the given throwable matches the expected data,
* fail (throws an exception) if it does not.
*
* @param \Throwable $throwable
* @param string $expectedClass
* @param string $expectedMsg
* @param int $expectedCode
*/
protected function checkThrowable($throwable, $expectedClass, $expectedMsg, $expectedCode)
{
if (!($throwable instanceof $expectedClass)) {
$this->fail(sprintf(
"Exception of class '$expectedClass' expected to be thrown, but class '%s' was caught",
get_class($throwable)
));
}

if (null !== $expectedMsg && $throwable->getMessage() !== $expectedMsg) {
$this->fail(sprintf(
"Exception of class '$expectedClass' expected to have message '$expectedMsg', but actual message was '%s'",
$throwable->getMessage()
));
}

if (null !== $expectedCode && $throwable->getCode() !== $expectedCode) {
$this->fail(sprintf(
"Exception of class '$expectedClass' expected to have code '$expectedCode', but actual code was '%s'",
$throwable->getCode()
));
}

$this->assertTrue(true); // increment assertion counter
}
}
4 changes: 2 additions & 2 deletions tests/data/claypit/tests/powers/PowerUpCest.php
Expand Up @@ -4,7 +4,7 @@ class PowerUpCest
{
public function iHaveNoPower(PowerGuy $I)
{
$I->expectException('Exception', function() use ($I) {
$I->expectThrowable('Exception', function() use ($I) {
$I->gotThePower();
});
}
Expand All @@ -22,4 +22,4 @@ protected function drinkBluePotion(\Codeception\Module\PowerHelper $helper)
{
$helper->_reconfigure(['has_power' => true]);
}
}
}
2 changes: 1 addition & 1 deletion tests/data/snapshots/tests/SnapshotDataCest.php
Expand Up @@ -18,7 +18,7 @@ public function loadSnapshotAndSkipRefresh(DataTester $I, UserSnapshot $snapshot
]);

$snapshot->shouldRefreshSnapshot(false);
$I->expectException(\PHPUnit\Framework\AssertionFailedError::class, function() use ($snapshot) {
$I->expectThrowable(\PHPUnit\Framework\AssertionFailedError::class, function() use ($snapshot) {
$snapshot->assert();
});
}
Expand Down
@@ -1,4 +1,4 @@
<?php //[STAMP] 2b32c3ce371d5de018a4be8681f34f2c
<?php //[STAMP] 4cc078c474a72c60a2ef0492bd2e4c0b
namespace _generated;

// This class was automatically generated by build task
Expand Down Expand Up @@ -565,13 +565,49 @@ public function fail($message) {
*
* @param $exception string or \Exception
* @param $callback
*
* @deprecated Use expectThrowable instead
* @see \Codeception\Module\Asserts::expectException()
*/
public function expectException($exception, $callback) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('expectException', func_get_args()));
}


/**
* [!] Method is generated. Documentation taken from corresponding module.
*
* Handles and checks throwables (Exceptions/Errors) called inside the callback function.
* Either throwable class name or throwable instance should be provided.
*
* ```php
* <?php
* $I->expectThrowable(MyThrowable::class, function() {
* $this->doSomethingBad();
* });
*
* $I->expectThrowable(new MyException(), function() {
* $this->doSomethingBad();
* });
* ```
* If you want to check message or throwable code, you can pass them with throwable instance:
* ```php
* <?php
* // will check that throwable MyError is thrown with "Don't do bad things" message
* $I->expectThrowable(new MyError("Don't do bad things"), function() {
* $this->doSomethingBad();
* });
* ```
*
* @param $throwable string of \Throwable
* @param $callback
* @see \Codeception\Module\Asserts::expectThrowable()
*/
public function expectThrowable($throwable, $callback) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('expectThrowable', func_get_args()));
}


/**
* [!] Method is generated. Documentation taken from corresponding module.
*
Expand Down