phrozenbyte / phpunit-throwable-asserts
提供各种与Throwable相关的PHPUnit断言。
Requires
- php: ^7.2 || ^8.0
Requires (Dev)
- incompass/coverage: ^1.0
- mockery/mockery: ^1.3
- php-parallel-lint/php-parallel-lint: ^1.2
- phpunit/phpunit: ^8
- psalm/plugin-mockery: ^0.7.0
- psalm/plugin-phpunit: ^0.15.1
- symfony/yaml: ^4.4
- vimeo/psalm: ^4.5
Conflicts
- phpunit/phpunit: <8.0
This package is auto-updated.
Last update: 2024-09-16 01:42:48 UTC
README
PHPUnitThrowableAssertions 是一个小的 PHPUnit 扩展,用于断言可调用对象是否抛出特定的异常、错误或Throwable。
这个PHPUnit扩展允许开发者使用更直观的“断言为”方法,在单个断言中测试可调用对象是否抛出异常、错误和其他Throwable。它是PHPUnit内置的 expectException()、expectExceptionMessage() 和 expectExceptionCode() 方法的替代品,但功能更强大。
你需要更多PHPUnit约束?查看 PHPUnitArrayAssertions!它引入了各种断言,可以在单个断言中测试PHP数组和类似数组的数组数据。PHPUnit扩展通常用于API测试,用于断言API结果是否符合某些标准——包括其结构和数据。
由 Daniel Rudolf 制作。 PHPUnitThrowableAssertions 是免费和开源软件,根据 MIT许可证 发布。
目录
安装
PHPUnitThrowableAssertions 可在 Packagist.org 上找到,并可以使用 Composer 进行安装
composer require --dev phrozenbyte/phpunit-throwable-asserts
此PHPUnit扩展最初是为PHPUnit 8编写的,但应与任何后续的PHPUnit版本兼容。如果不兼容,请勿犹豫,在GitHub上打开一个 新问题,或者,更好的是,创建一个包含建议修复的Pull Request。
使用方法
使用 PHPUnitThrowableAssertions 有三种(等效的)方法
- 通过使用静态 类
PhrozenByte\PHPUnitThrowableAssertions\Assert - 通过在您的测试用例中使用 特质
PhrozenByte\PHPUnitThrowableAssertions\ThrowableAssertsTrait - 通过创建新的 约束实例 (
PhrozenByte\PHPUnitThrowableAssertions\Constraint\…)
所有选项都完全相同。创建新的约束实例对于高级断言很有用,例如与 PHPUnit\Framework\Constraint\LogicalAnd 一起使用。
如果您想向您的可调用对象传递参数,您可能想使用 CallableProxy。如果您想访问可调用对象的返回值或可能抛出的Throwable,请使用 CachedCallableProxy(特别是其 getReturnValue() 和 getThrowable() 方法)。使用 CallableProxy 可以大大改进错误处理。
如上所述,PHPUnitThrowableAssertions 是 PHPUnit 内置的 expectException() 的更强大替代方案。然而,请注意,PHPUnit 内置的 expectExceptionMessage() 匹配子字符串(即 $this->expectExceptionMessage('test') 不仅匹配消息 "test",还匹配 "This is a test"),而 PHPUnitThrowableAssertions 默认通过相等性进行检查(即 $message = 'test' 只匹配消息 "test")。但是,PHPUnitThrowableAssertions 允许您不仅使用字符串,还可以使用任意约束。因此,例如,为了实现子字符串匹配,请传递一个 PHPUnit\Framework\Constraint\StringContains 约束的实例(即 $message = $this->stringContains('test') 也匹配消息 "This is a test")。
约束 CallableThrows
CallableThrows 约束断言一个 Callable 抛出一个特定的 Throwable。
该约束调用给定的 Callable(参数 $callable)并捕获与给定基类(参数 $throwableBaseClassName,默认为 Throwable)匹配的任何 Throwable。任何其他 Throwable 都不会被捕获。然后它断言 Throwable 的类(可选参数 $throwableClassName,默认为 Throwable)、消息(可选参数 $throwableMessage,默认为 null)和代码(可选参数 $throwableCode,默认为 null)与预期匹配,否则抛出 ExpectationFailedException。异常消息可以是字符串,需要精确匹配,也可以是任意 Constraint(例如 PHPUnit\Framework\Constraint\StringContains)以匹配异常消息。约束可选地需要类名的精确匹配(可选参数 $throwableExactMatch,默认为 false)。
ThrowableAssertsTrait 特性公开了两个公共方法用于 CallableThrows 约束:使用 ThrowableAssertsTrait::assertCallableThrows() 进行断言,并使用 ThrowableAssertsTrait::callableThrows() 创建 CallableThrows 约束的新实例。
使用方法
// using `PhrozenByte\PHPUnitThrowableAsserts\ThrowableAssertsTrait` trait ThrowableAssertsTrait::assertCallableThrows( callable $callable, // the Callable to call string $throwableClassName = Throwable::class, // assert that a Throwable of the given class is thrown Constraint|string $throwableMessage = null, // assert that its message matches the given constraint int|string $throwableCode = null, // assert that its code matches the given one bool $throwableExactMatch = false, // whether an exact match of the class name is required string $throwableBaseClassName = Throwable::class, // catch all Throwables of the given class string $message = '' // additional information about the test ); // using new instance of `PhrozenByte\PHPUnitThrowableAsserts\Constraint\CallableThrows` new CallableThrows( string $className = Throwable::class, Constraint|string $message = null, int|string $code = null, bool $exactMatch = false, string $baseClassName = Throwable::class );
示例
$controller = new BookController(); $bookName = "The Hitchhiker's Guide to the Galaxy"; $bookReleaseDate = '1979-10-12'; $this->assertCallableThrows( $this->callableProxy([ $controller, 'create' ], $bookName, $bookReleaseDate), BookAlreadyExistsException::class, 'Unable to create book: Book already exists' );
调试
$service = new HitchhikersGuideService(); $towel = false; $answer = 42; $this->assertCallableThrows( static function () use ($service, $towel, $answer) { $service->checkAnswer($answer); // throws a OpaqueAnswerException $service->checkTowel($towel); // throws a PanicException (unreachable code) }, PanicException::class, 'I forgot my towel' ); // Will fail with the following message: // // Failed asserting that {closure}() throws a PanicException whose message is 'Time to panic'. // Encountered invalid OpaqueAnswerException: I do not understand. // --- Expected // +++ Actual // @@ @@ // -'Time to panic' // +'I do not understand'
约束 CallableThrowsNot
CallableThrowsNot 约束断言一个 Callable 不抛出特定的 Throwable。它可以作为 PHPUnit 内置的 expectNotToPerformAssertions() 方法的更具体替代方案。
该约束调用给定的 Callable(参数 $callable)并捕获与给定类(可选参数 $throwableClassName,默认为 Throwable)、消息(可选参数 $throwableMessage,默认为 null)和代码(可选参数 $throwableCode,默认为 null)匹配的任何 Throwable。所有条件都必须匹配,否则将重新抛出 Throwable。异常消息可以是字符串,需要精确匹配,也可以是任意 Constraint(例如 PHPUnit\Framework\Constraint\StringContains)以匹配异常消息。约束可选地需要类名的精确匹配(可选参数 $throwableExactMatch,默认为 false)。
这 不 与否定 CallableThrows 约束相同,后者消费所有不匹配的 Throwable 并抛出 ExpectationFailedException。CallableThrowsNot 将重新抛出任何不匹配的 Throwable。只有在 Callable 抛出匹配所有给定条件的 Throwable 时才会抛出 ExpectationFailedException。
ThrowableAssertsTrait 特性公开了两个公共方法用于 CallableThrowsNot 约束:使用 ThrowableAssertsTrait::assertCallableThrowsNot() 进行断言,并使用 ThrowableAssertsTrait::callableThrowsNot() 创建 CallableThrowsNot 约束的新实例。
使用方法
// using `PhrozenByte\PHPUnitThrowableAsserts\ThrowableAssertsTrait` trait ThrowableAssertsTrait::assertCallableThrowsNot( callable $callable, // the Callable to call string $throwableClassName = Throwable::class, // assert that no Throwable of the given class is thrown Constraint|string $throwableMessage = null, // catch Throwables matching the given message constraint only int|string $throwableCode = null, // catch Throwables matching the given code only bool $throwableExactMatch = false, // whether only Throwables of the given class are caught string $message = '' // additional information about the test ); // using new instance of `PhrozenByte\PHPUnitThrowableAsserts\Constraint\CallableThrowsNot` new CallableThrowsNot( string $className = Throwable::class, Constraint|string $message = null, int|string $code = null, bool $exactMatch = false );
示例
$controller = CharacterController(); $character = 'Prostetnik Vogon Jeltz'; $this->assertCallableThrowsNot( $this->callableProxy([ $controller, 'meet' ], $character), VogonWantsToReadPoetException::class );
调试
$controller = new BookController(); $bookName = "The Hitchhiker's Guide to the Galaxy"; $bookReleaseDate = '1979-10-12'; $this->assertCallableThrowsNot( $this->callableProxy([ $controller, 'create' ], $bookName, $bookReleaseDate), BookAlreadyExistsException::class ); // Will fail with the following message: // // Failed asserting that BookController::create() does not throw a BookAlreadyExistsException // Encountered invalid BookAlreadyExistsException: Unable to create book: Book already exists
CallableProxy 和 CachedCallableProxy
PHPUnitThrowableAsserts 在调用可调用对象(Callable)时不需要参数,并且由于PHPUnit如何评估值,因此会丢弃可能的返回值。针对此问题的一个解决方案是使用具有变量继承的匿名函数。作为一个简洁的替代方案,PHPUnitThrowableAsserts 提供了CallableProxy 和 CachedCallableProxy 辅助类。
这两个辅助类在其构造函数中接收要调用的可调用对象(参数 $callable)和要传递的参数(任何后续参数,变长参数 $arguments)。此外,它们还实现了PHPUnit的 PHPUnit\Framework\SelfDescribing 接口和 toString() 方法,通过允许 PHPUnitThrowableAsserts 更好地指定调用方法,从而提高了错误处理能力。CachedCallableProxy 还实现了 getReturnValue() 和 getThrowable() 方法。getReturnValue() 返回可调用对象上次调用的缓存返回值,而 getThrowable() 返回可能抛出的 Throwable。
ThrowableAssertsTrait 特性公开了两个公共方法来创建 CallableProxy 和 CachedCallableProxy 的实例:使用 ThrowableAssertsTrait::callableProxy() 创建一个新的 CallableProxy 实例,或者使用 ThrowableAssertsTrait::cachedCallableProxy() 创建一个新的 CachedCallableProxy 实例。
使用方法
// create new instance of `PhrozenByte\PHPUnitThrowableAsserts\CallableProxy` // using the `PhrozenByte\PHPUnitThrowableAsserts\ThrowableAssertsTrait` trait ThrowableAssertsTrait::callableProxy( callable $callable, // the Callable to invoke mixed ...$arguments // the arguments to pass to the Callable ); // create new instance of `PhrozenByte\PHPUnitThrowableAsserts\CachedCallableProxy` // using the `PhrozenByte\PHPUnitThrowableAsserts\ThrowableAssertsTrait` trait $proxy = ThrowableAssertsTrait::cachedCallableProxy( callable $callable, // the Callable to invoke mixed ...$arguments // the arguments to pass to the Callable ); // get return value of the Callable (`CachedCallableProxy` only) $proxy->getReturnValue(); // get a possibly thrown Throwable (`CachedCallableProxy` only) $proxy->getThrowable();
示例
$computer = new DeepThought(); $question = 'What is the Answer to the Ultimate Question of Life, the Universe, and Everything?'; // using `PhrozenByte\PHPUnitThrowableAsserts\CallableProxy` // if the assertion fails, `ExpectationFailedException`'s message will point to DeepThought::ask() as source $askQuestion = $this->cachedCallableProxy([ $computer, 'ask' ], $question); $this->assertCallableThrowsNot($askQuestion); $answer = $askQuestion->getReturnValue(); // using anonymous function // if the assertion fails, `ExpectationFailedException` will just name {closure} as source $answer = null; $this->assertCallableThrowsNot(static function () use ($computer, $question, &$answer) { // use variable reference to pass the return value $answer = $computer->ask($question); });
PHP错误、警告和通知
PHPUnit 默认将 PHP 错误(E_RECOVERABLE_ERROR)、警告(E_WARNING 和 E_USER_WARNING)、通知(E_NOTICE、E_USER_NOTICE 和 E_STRICT)和弃用通知(E_DEPRECATED 和 E_USER_DEPRECATED)转换为 PHPUnit\Framework\Error\… 异常(分别为 …\Error、…\Warning、…\Notice 和 …\Deprecated)。这使得您可以使用 PHPUnitThrowableAssertions 的 assertCallableThrows() 和 assertCallableThrowsNot() 断言来捕获任何PHP错误;只需使用 PHPUnit\Framework\Error\… 中的一个类即可。
请勿将PHP错误与PHP 7.0中引入的 Error 类 混淆。后者已经是一个 Throwable,并且可以像往常一样捕获。
示例
$this->assertCallableThrows( static function () { // triggers a E_NOTICE PHP error echo $undefinedVariable; }, \PHPUnit\Framework\Error\Notice::class, 'Undefined variable: undefinedVariable' );