Skip to content

Commit 11fc397

Browse files
burned42DavertMik
authored andcommitted
Asserts: Add expectThrowable() method (#5213)
* Asserts: add expectThrowable() With this method you can not only test Exceptions, like with expectException, but also Errors. Signed-off-by: Bernd Stellwag <[email protected]> * Asserts tests: set up test class in setUp() Signed-off-by: Bernd Stellwag <[email protected]> * Asserts: mention expectThrowable in the documentation Signed-off-by: Bernd Stellwag <[email protected]> * Asserts: add type hint Signed-off-by: Bernd Stellwag <[email protected]> * Asserts: since we marked expectedException as deprecated, use expectThrowable instead Signed-off-by: Bernd Stellwag <[email protected]> * fix typos Signed-off-by: Bernd Stellwag <[email protected]> * Asserts: try to make expectThrowable compatible with PHP 5.6 again Signed-off-by: Bernd Stellwag <[email protected]> * don't use @ExpectedException annotation as it will get deprecated see sebastianbergmann/phpunit#3332
1 parent 93e89ec commit 11fc397

File tree

2 files changed

+182
-71
lines changed

2 files changed

+182
-71
lines changed

src/Codeception/Module/Asserts.php

Lines changed: 86 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -455,40 +455,100 @@ public function fail($message)
455455
*
456456
* @param $exception string or \Exception
457457
* @param $callback
458+
*
459+
* @deprecated Use expectThrowable instead
458460
*/
459461
public function expectException($exception, $callback)
460462
{
461-
$code = null;
462-
$msg = null;
463-
if (is_object($exception)) {
464-
/** @var $exception \Exception **/
465-
$class = get_class($exception);
466-
$msg = $exception->getMessage();
467-
$code = $exception->getCode();
463+
$this->expectThrowable($exception, $callback);
464+
}
465+
466+
/**
467+
* Handles and checks throwables (Exceptions/Errors) called inside the callback function.
468+
* Either throwable class name or throwable instance should be provided.
469+
*
470+
* ```php
471+
* <?php
472+
* $I->expectThrowable(MyThrowable::class, function() {
473+
* $this->doSomethingBad();
474+
* });
475+
*
476+
* $I->expectThrowable(new MyException(), function() {
477+
* $this->doSomethingBad();
478+
* });
479+
* ```
480+
* If you want to check message or throwable code, you can pass them with throwable instance:
481+
* ```php
482+
* <?php
483+
* // will check that throwable MyError is thrown with "Don't do bad things" message
484+
* $I->expectThrowable(new MyError("Don't do bad things"), function() {
485+
* $this->doSomethingBad();
486+
* });
487+
* ```
488+
*
489+
* @param $throwable string or \Throwable
490+
* @param $callback
491+
*/
492+
public function expectThrowable($throwable, $callback)
493+
{
494+
if (is_object($throwable)) {
495+
/** @var $throwable \Throwable */
496+
$class = get_class($throwable);
497+
$msg = $throwable->getMessage();
498+
$code = $throwable->getCode();
468499
} else {
469-
$class = $exception;
500+
$class= $throwable;
501+
$msg = null;
502+
$code = null;
470503
}
504+
471505
try {
472506
$callback();
473-
} catch (\Exception $e) {
474-
if (!$e instanceof $class) {
475-
$this->fail(sprintf("Exception of class $class expected to be thrown, but %s caught", get_class($e)));
476-
}
477-
if (null !== $msg and $e->getMessage() !== $msg) {
478-
$this->fail(sprintf(
479-
"Exception of $class expected to be '$msg', but actual message was '%s'",
480-
$e->getMessage()
481-
));
482-
}
483-
if (null !== $code and $e->getCode() !== $code) {
484-
$this->fail(sprintf(
485-
"Exception of $class expected to have code $code, but actual code was %s",
486-
$e->getCode()
487-
));
488-
}
489-
$this->assertTrue(true); // increment assertion counter
507+
} catch (\Exception $t) {
508+
$this->checkThrowable($t, $class, $msg, $code);
509+
510+
return;
511+
} catch (\Throwable $t) {
512+
$this->checkThrowable($t, $class, $msg, $code);
513+
490514
return;
491515
}
492-
$this->fail("Expected exception of $class to be thrown, but nothing was caught");
516+
517+
$this->fail("Expected throwable of class '$class' to be thrown, but nothing was caught");
518+
}
519+
520+
/**
521+
* Check if the given throwable matches the expected data,
522+
* fail (throws an exception) if it does not.
523+
*
524+
* @param \Throwable $throwable
525+
* @param string $expectedClass
526+
* @param string $expectedMsg
527+
* @param int $expectedCode
528+
*/
529+
protected function checkThrowable($throwable, $expectedClass, $expectedMsg, $expectedCode)
530+
{
531+
if (!($throwable instanceof $expectedClass)) {
532+
$this->fail(sprintf(
533+
"Exception of class '$expectedClass' expected to be thrown, but class '%s' was caught",
534+
get_class($throwable)
535+
));
536+
}
537+
538+
if (null !== $expectedMsg && $throwable->getMessage() !== $expectedMsg) {
539+
$this->fail(sprintf(
540+
"Exception of class '$expectedClass' expected to have message '$expectedMsg', but actual message was '%s'",
541+
$throwable->getMessage()
542+
));
543+
}
544+
545+
if (null !== $expectedCode && $throwable->getCode() !== $expectedCode) {
546+
$this->fail(sprintf(
547+
"Exception of class '$expectedClass' expected to have code '$expectedCode', but actual code was '%s'",
548+
$throwable->getCode()
549+
));
550+
}
551+
552+
$this->assertTrue(true); // increment assertion counter
493553
}
494554
}
Lines changed: 96 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,124 @@
11
<?php
22
class AssertsTest extends \PHPUnit\Framework\TestCase
33
{
4+
/** @var \Codeception\Module\Asserts */
5+
protected $module;
6+
7+
public function setUp()
8+
{
9+
$this->module = new \Codeception\Module\Asserts(make_container());
10+
}
11+
412
public function testAsserts()
513
{
6-
$module = new \Codeception\Module\Asserts(make_container());
7-
$module->assertEquals(1, 1);
8-
$module->assertContains(1, [1, 2]);
9-
$module->assertSame(1, 1);
10-
$module->assertNotSame(1, '1');
11-
$module->assertRegExp('/^[\d]$/', '1');
12-
$module->assertNotRegExp('/^[a-z]$/', '1');
13-
$module->assertStringStartsWith('fo', 'foo');
14-
$module->assertStringStartsNotWith('ba', 'foo');
15-
$module->assertEmpty([]);
16-
$module->assertNotEmpty([1]);
17-
$module->assertNull(null);
18-
$module->assertNotNull('');
19-
$module->assertNotNull(false);
20-
$module->assertNotNull(0);
21-
$module->assertTrue(true);
22-
$module->assertNotTrue(false);
23-
$module->assertNotTrue(null);
24-
$module->assertNotTrue('foo');
25-
$module->assertFalse(false);
26-
$module->assertNotFalse(true);
27-
$module->assertNotFalse(null);
28-
$module->assertNotFalse('foo');
29-
$module->assertFileExists(__FILE__);
30-
$module->assertFileNotExists(__FILE__ . '.notExist');
31-
$module->assertInstanceOf('Exception', new Exception());
32-
$module->assertInternalType('integer', 5);
33-
$module->assertArrayHasKey('one', ['one' => 1, 'two' => 2]);
34-
$module->assertArraySubset(['foo' => [1]], ['foo' => [1, 2]]);
35-
$module->assertCount(3, [1, 2, 3]);
14+
$this->module->assertEquals(1, 1);
15+
$this->module->assertContains(1, [1, 2]);
16+
$this->module->assertSame(1, 1);
17+
$this->module->assertNotSame(1, '1');
18+
$this->module->assertRegExp('/^[\d]$/', '1');
19+
$this->module->assertNotRegExp('/^[a-z]$/', '1');
20+
$this->module->assertStringStartsWith('fo', 'foo');
21+
$this->module->assertStringStartsNotWith('ba', 'foo');
22+
$this->module->assertEmpty([]);
23+
$this->module->assertNotEmpty([1]);
24+
$this->module->assertNull(null);
25+
$this->module->assertNotNull('');
26+
$this->module->assertNotNull(false);
27+
$this->module->assertNotNull(0);
28+
$this->module->assertTrue(true);
29+
$this->module->assertNotTrue(false);
30+
$this->module->assertNotTrue(null);
31+
$this->module->assertNotTrue('foo');
32+
$this->module->assertFalse(false);
33+
$this->module->assertNotFalse(true);
34+
$this->module->assertNotFalse(null);
35+
$this->module->assertNotFalse('foo');
36+
$this->module->assertFileExists(__FILE__);
37+
$this->module->assertFileNotExists(__FILE__ . '.notExist');
38+
$this->module->assertInstanceOf('Exception', new Exception());
39+
$this->module->assertInternalType('integer', 5);
40+
$this->module->assertArrayHasKey('one', ['one' => 1, 'two' => 2]);
41+
$this->module->assertArraySubset(['foo' => [1]], ['foo' => [1, 2]]);
42+
$this->module->assertCount(3, [1, 2, 3]);
3643
}
3744

3845
public function testExceptions()
3946
{
40-
$module = new \Codeception\Module\Asserts(make_container());
41-
$module->expectException('Exception', function () {
47+
$this->module->expectException('Exception', function () {
4248
throw new Exception;
4349
});
44-
$module->expectException(new Exception('here'), function () {
50+
$this->module->expectException(new Exception('here'), function () {
4551
throw new Exception('here');
4652
});
47-
$module->expectException(new Exception('here', 200), function () {
53+
$this->module->expectException(new Exception('here', 200), function () {
4854
throw new Exception('here', 200);
4955
});
5056
}
5157

52-
/**
53-
* @expectedException PHPUnit\Framework\AssertionFailedError
54-
*/
5558
public function testExceptionFails()
5659
{
57-
$module = new \Codeception\Module\Asserts(make_container());
58-
$module->expectException(new Exception('here', 200), function () {
60+
$this->expectException(PHPUnit\Framework\AssertionFailedError::class);
61+
62+
$this->module->expectException(new Exception('here', 200), function () {
5963
throw new Exception('here', 2);
6064
});
6165
}
6266

63-
/**
64-
* @expectedException PHPUnit\Framework\AssertionFailedError
65-
* @expectedExceptionMessageRegExp /RuntimeException/
66-
*/
6767
public function testOutputExceptionTimeWhenNothingCaught()
6868
{
69-
$module = new \Codeception\Module\Asserts(make_container());
70-
$module->expectException(RuntimeException::class, function () {
69+
$this->expectException(\PHPUnit\Framework\AssertionFailedError::class);
70+
$this->expectExceptionMessageRegExp('/RuntimeException/');
71+
72+
$this->module->expectException(RuntimeException::class, function () {
73+
});
74+
}
75+
76+
public function testExpectThrowable()
77+
{
78+
$this->module->expectThrowable('Exception', function () {
79+
throw new Exception();
80+
});
81+
$this->module->expectThrowable(new Exception('here'), function () {
82+
throw new Exception('here');
83+
});
84+
$this->module->expectThrowable(new Exception('here', 200), function () {
85+
throw new Exception('here', 200);
86+
});
87+
}
88+
89+
public function testExpectThrowableFailOnDifferentClass()
90+
{
91+
$this->expectException(\PHPUnit\Framework\AssertionFailedError::class);
92+
93+
$this->module->expectThrowable(new RuntimeException(), function () {
94+
throw new Exception();
95+
});
96+
}
97+
98+
public function testExpectThrowableFailOnDifferentMessage()
99+
{
100+
$this->expectException(\PHPUnit\Framework\AssertionFailedError::class);
101+
102+
$this->module->expectThrowable(new Exception('foo', 200), function () {
103+
throw new Exception('bar', 200);
104+
});
105+
}
106+
107+
public function testExpectThrowableFailOnDifferentCode()
108+
{
109+
$this->expectException(\PHPUnit\Framework\AssertionFailedError::class);
110+
111+
$this->module->expectThrowable(new Exception('foobar', 200), function () {
112+
throw new Exception('foobar', 2);
113+
});
114+
}
115+
116+
public function testExpectThrowableFailOnNothingCaught()
117+
{
118+
$this->expectException(\PHPUnit\Framework\AssertionFailedError::class);
119+
$this->expectExceptionMessageRegExp('/RuntimeException/');
120+
121+
$this->module->expectThrowable(RuntimeException::class, function () {
71122
});
72123
}
73124
}

0 commit comments

Comments
 (0)