mirror of
https://github.com/pestphp/pest.git
synced 2026-03-07 08:17:22 +01:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 075c31bc78 | |||
| 2125bf9668 | |||
| dbf3c0a8cf | |||
| c776bcf86d |
@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [v1.17.0 (2021-08-26)](https://github.com/pestphp/pest/compare/v1.16.0...v1.17.0)
|
||||
### Added
|
||||
- `toThrow` expectation ([#361](https://github.com/pestphp/pest/pull/361))
|
||||
|
||||
## [v1.16.0 (2021-08-19)](https://github.com/pestphp/pest/compare/v1.15.0...v1.16.0)
|
||||
### Added
|
||||
- Support for new parallel options ([#369](https://github.com/pestphp/pest/pull/369))
|
||||
|
||||
@ -5,13 +5,19 @@ declare(strict_types=1);
|
||||
namespace Pest;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use Pest\Concerns\Extendable;
|
||||
use Pest\Concerns\RetrievesValues;
|
||||
use Pest\Support\Arr;
|
||||
use Pest\Support\NullClosure;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
use ReflectionFunction;
|
||||
use ReflectionNamedType;
|
||||
use SebastianBergmann\Exporter\Exporter;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -749,6 +755,56 @@ final class Expectation
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that executing value throws an exception.
|
||||
*
|
||||
* @param (Closure(Throwable): mixed)|string $exception
|
||||
*/
|
||||
public function toThrow($exception, string $exceptionMessage = null): Expectation
|
||||
{
|
||||
$callback = NullClosure::create();
|
||||
|
||||
if ($exception instanceof Closure) {
|
||||
$callback = $exception;
|
||||
$parameters = (new ReflectionFunction($exception))->getParameters();
|
||||
|
||||
if (1 !== count($parameters)) {
|
||||
throw new InvalidArgumentException('The given closure must have a single parameter type-hinted as the class string.');
|
||||
}
|
||||
|
||||
if (!($type = $parameters[0]->getType()) instanceof ReflectionNamedType) {
|
||||
throw new InvalidArgumentException('The given closure\'s parameter must be type-hinted as the class string.');
|
||||
}
|
||||
|
||||
$exception = $type->getName();
|
||||
}
|
||||
|
||||
try {
|
||||
($this->value)();
|
||||
} catch (Throwable $e) { // @phpstan-ignore-line
|
||||
if (!class_exists($exception)) {
|
||||
Assert::assertStringContainsString($exception, $e->getMessage());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($exceptionMessage !== null) {
|
||||
Assert::assertStringContainsString($exceptionMessage, $e->getMessage());
|
||||
}
|
||||
|
||||
Assert::assertInstanceOf($exception, $e);
|
||||
$callback($e);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (!class_exists($exception)) {
|
||||
throw new ExpectationFailedException("Exception with message \"{$exception}\" not thrown.");
|
||||
}
|
||||
|
||||
throw new ExpectationFailedException("Exception \"{$exception}\" not thrown.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the given value.
|
||||
*
|
||||
|
||||
@ -6,7 +6,7 @@ namespace Pest;
|
||||
|
||||
function version(): string
|
||||
{
|
||||
return '1.16.0';
|
||||
return '1.17.0';
|
||||
}
|
||||
|
||||
function testDirectory(string $file = ''): string
|
||||
|
||||
@ -22,7 +22,7 @@ use PHPUnit\Framework\TestCase;
|
||||
final class TestRepository
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @var non-empty-string
|
||||
*/
|
||||
private const SEPARATOR = '>>>';
|
||||
|
||||
|
||||
@ -449,6 +449,19 @@
|
||||
✓ failures
|
||||
✓ not failures
|
||||
|
||||
PASS Tests\Features\Expect\toThrow
|
||||
✓ passes
|
||||
✓ failures 1
|
||||
✓ failures 2
|
||||
✓ failures 3
|
||||
✓ failures 4
|
||||
✓ failures 5
|
||||
✓ failures 6
|
||||
✓ failures 7
|
||||
✓ not failures
|
||||
✓ closure missing parameter
|
||||
✓ closure missing type-hint
|
||||
|
||||
PASS Tests\Features\Helpers
|
||||
✓ it can set/get properties on $this
|
||||
✓ it throws error if property do not exist
|
||||
@ -649,5 +662,5 @@
|
||||
✓ it is a test
|
||||
✓ it uses correct parent class
|
||||
|
||||
Tests: 4 incompleted, 9 skipped, 421 passed
|
||||
Tests: 4 incompleted, 9 skipped, 432 passed
|
||||
|
||||
60
tests/Features/Expect/toThrow.php
Normal file
60
tests/Features/Expect/toThrow.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
use PHPUnit\Framework\ExpectationFailedException;
|
||||
|
||||
test('passes', function () {
|
||||
expect(function () { throw new RuntimeException(); })->toThrow(RuntimeException::class);
|
||||
expect(function () { throw new RuntimeException(); })->toThrow(Exception::class);
|
||||
expect(function () { throw new RuntimeException(); })->toThrow(function (RuntimeException $e) {});
|
||||
expect(function () { throw new RuntimeException('actual message'); })->toThrow(function (Exception $e) {
|
||||
expect($e->getMessage())->toBe('actual message');
|
||||
});
|
||||
expect(function () {})->not->toThrow(Exception::class);
|
||||
expect(function () { throw new RuntimeException('actual message'); })->toThrow('actual message');
|
||||
expect(function () { throw new Exception(); })->not->toThrow(RuntimeException::class);
|
||||
expect(function () { throw new RuntimeException('actual message'); })->toThrow(RuntimeException::class, 'actual message');
|
||||
expect(function () { throw new RuntimeException('actual message'); })->toThrow(function (RuntimeException $e) {}, 'actual message');
|
||||
});
|
||||
|
||||
test('failures 1', function () {
|
||||
expect(function () {})->toThrow(RuntimeException::class);
|
||||
})->throws(ExpectationFailedException::class, 'Exception "' . RuntimeException::class . '" not thrown.');
|
||||
|
||||
test('failures 2', function () {
|
||||
expect(function () {})->toThrow(function (RuntimeException $e) {});
|
||||
})->throws(ExpectationFailedException::class, 'Exception "' . RuntimeException::class . '" not thrown.');
|
||||
|
||||
test('failures 3', function () {
|
||||
expect(function () { throw new Exception(); })->toThrow(function (RuntimeException $e) {});
|
||||
})->throws(ExpectationFailedException::class, 'Failed asserting that Exception Object');
|
||||
|
||||
test('failures 4', function () {
|
||||
expect(function () { throw new Exception('actual message'); })
|
||||
->toThrow(function (Exception $e) {
|
||||
expect($e->getMessage())->toBe('expected message');
|
||||
});
|
||||
})->throws(ExpectationFailedException::class, 'Failed asserting that two strings are identical');
|
||||
|
||||
test('failures 5', function () {
|
||||
expect(function () { throw new Exception('actual message'); })->toThrow('expected message');
|
||||
})->throws(ExpectationFailedException::class, 'Failed asserting that \'actual message\' contains "expected message".');
|
||||
|
||||
test('failures 6', function () {
|
||||
expect(function () {})->toThrow('actual message');
|
||||
})->throws(ExpectationFailedException::class, 'Exception with message "actual message" not thrown');
|
||||
|
||||
test('failures 7', function () {
|
||||
expect(function () { throw new RuntimeException('actual message'); })->toThrow(RuntimeException::class, 'expected message');
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('not failures', function () {
|
||||
expect(function () { throw new RuntimeException(); })->not->toThrow(RuntimeException::class);
|
||||
})->throws(ExpectationFailedException::class);
|
||||
|
||||
test('closure missing parameter', function () {
|
||||
expect(function () {})->toThrow(function () {});
|
||||
})->throws(InvalidArgumentException::class, 'The given closure must have a single parameter type-hinted as the class string.');
|
||||
|
||||
test('closure missing type-hint', function () {
|
||||
expect(function () {})->toThrow(function ($e) {});
|
||||
})->throws(InvalidArgumentException::class, 'The given closure\'s parameter must be type-hinted as the class string.');
|
||||
Reference in New Issue
Block a user