{"id":15978973,"url":"https://github.com/phrozenbyte/phpunit-throwable-asserts","last_synced_at":"2025-04-04T18:14:54.433Z","repository":{"id":57041106,"uuid":"329736782","full_name":"PhrozenByte/phpunit-throwable-asserts","owner":"PhrozenByte","description":"Provides various Throwable-related PHPUnit assertions.","archived":false,"fork":false,"pushed_at":"2021-02-15T17:53:11.000Z","size":106,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-10T08:39:09.219Z","etag":null,"topics":["php","phpunit","phpunit-assertions","phpunit-constraint","phpunit-extension"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PhrozenByte.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-01-14T21:18:02.000Z","updated_at":"2021-02-15T17:46:50.000Z","dependencies_parsed_at":"2022-08-24T01:10:55.658Z","dependency_job_id":null,"html_url":"https://github.com/PhrozenByte/phpunit-throwable-asserts","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhrozenByte%2Fphpunit-throwable-asserts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhrozenByte%2Fphpunit-throwable-asserts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhrozenByte%2Fphpunit-throwable-asserts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PhrozenByte%2Fphpunit-throwable-asserts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PhrozenByte","download_url":"https://codeload.github.com/PhrozenByte/phpunit-throwable-asserts/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247226190,"owners_count":20904465,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["php","phpunit","phpunit-assertions","phpunit-constraint","phpunit-extension"],"created_at":"2024-10-07T23:40:30.839Z","updated_at":"2025-04-04T18:14:54.409Z","avatar_url":"https://github.com/PhrozenByte.png","language":"PHP","readme":"PHPUnitThrowableAssertions\n==========================\n\n[![MIT license](https://raw.githubusercontent.com/PhrozenByte/phpunit-throwable-asserts/master/.github/license.svg)](https://github.com/PhrozenByte/phpunit-throwable-asserts/blob/master/LICENSE)\n[![Code coverage](https://raw.githubusercontent.com/PhrozenByte/phpunit-throwable-asserts/master/.github/coverage.svg)](https://github.com/PhrozenByte/phpunit-throwable-asserts)\n\n[`PHPUnitThrowableAssertions`](https://github.com/PhrozenByte/phpunit-throwable-asserts) is a small [PHPUnit](https://phpunit.de/) extension to assert that Callables do or do not throw a specific Exception, Error, or Throwable.\n\nThis PHPUnit extension allows developers to test whether Callables throw Exceptions, Errors and other Throwables in a single assertion using the more intuitive \"assert that\" approach. It's a replacement for PHPUnit's built-in `expectException()`, `expectExceptionMessage()` and `expectExceptionCode()` methods - just more powerful.\n\nYou want more PHPUnit constraints? Check out [`PHPUnitArrayAssertions`](https://github.com/PhrozenByte/phpunit-array-asserts)! It introduces various assertions to test PHP arrays and array-like data in a single assertion. The PHPUnit extension is often used for API testing to assert whether an API result matches certain criteria - regarding both its structure, and the data.\n\nMade with :heart: by [Daniel Rudolf](https://www.daniel-rudolf.de). `PHPUnitThrowableAssertions` is free and open source software, released under the terms of the [MIT license](https://github.com/PhrozenByte/phpunit-throwable-asserts/blob/master/LICENSE).\n\n**Table of contents:**\n\n1. [Install](#install)\n2. [Usage](#usage)\n    1. [Constraint `CallableThrows`](#constraint-callablethrows)\n    2. [Constraint `CallableThrowsNot`](#constraint-callablethrowsnot)\n    3. [`CallableProxy` and `CachedCallableProxy`](#callableproxy-and-cachedcallableproxy)\n    4. [PHP errors, warnings and notices](#php-errors-warnings-and-notices)\n\nInstall\n-------\n\n`PHPUnitThrowableAssertions` is available on [Packagist.org](https://packagist.org/packages/phrozenbyte/phpunit-throwable-asserts) and can be installed using [Composer](https://getcomposer.org/):\n\n```shell\ncomposer require --dev phrozenbyte/phpunit-throwable-asserts\n```\n\nThis PHPUnit extension was initially written for PHPUnit 8, but should work fine with any later PHPUnit version. If it doesn't, please don't hesitate to open a [new Issue on GitHub](https://github.com/PhrozenByte/phpunit-throwable-asserts/issues/new), or, even better, create a Pull Request with a proposed fix.\n\nUsage\n-----\n\nThere are three (equivalent) options to use `PHPUnitThrowableAssertions`:\n\n- By using the static [class `PhrozenByte\\PHPUnitThrowableAssertions\\Assert`](https://github.com/PhrozenByte/phpunit-throwable-asserts/blob/master/src/Assert.php)\n- By using the [trait `PhrozenByte\\PHPUnitThrowableAssertions\\ThrowableAssertsTrait`](https://github.com/PhrozenByte/phpunit-throwable-asserts/blob/master/src/ThrowableAssertsTrait.php) in your test case\n- By creating new [constraint instances](https://github.com/PhrozenByte/phpunit-throwable-asserts/tree/master/src/Constraint) (`PhrozenByte\\PHPUnitThrowableAssertions\\Constraint\\…`)\n\nAll options do exactly the same. Creating new constraint instances is useful for advanced assertions, e.g. together with `PHPUnit\\Framework\\Constraint\\LogicalAnd`.\n\nIf you want to pass arguments to your Callable, you might want to use [`CallableProxy`](#callableproxy-and-cachedcallableproxy). If you want to access the Callable's return value or a possibly thrown Throwable, use `CachedCallableProxy` instead (specifically its `getReturnValue()` and `getThrowable()` methods). Using `CallableProxy` vastly improves error handling.\n\nAs explained above, `PHPUnitThrowableAssertions` is a more powerful alternative to PHPUnit's built-in `expectException()`. However, please note that PHPUnit's built-in `expectExceptionMessage()` matches sub strings (i.e. `$this-\u003eexpectExceptionMessage('test')` doesn't just match the message `\"test\"`, but also `\"This is a test\"`), while `PHPUnitThrowableAssertions` checks for equality by default (i.e. `$message = 'test'` matches the message `\"test\"` only). However, `PHPUnitThrowableAssertions` allows you to not just use strings, but also arbitrary constraints. So, for example, to achieve sub string matching, pass an instance of the `PHPUnit\\Framework\\Constraint\\StringContains` constraint instead (i.e. `$message = $this-\u003estringContains('test')` also matches the message `\"This is a test\"`).\n\n### Constraint `CallableThrows`\n\nThe [`CallableThrows` constraint](https://github.com/PhrozenByte/phpunit-throwable-asserts/blob/master/src/Constraint/CallableThrows.php) asserts that a Callable throws a specific `Throwable`.\n\nThis constraint calls the given Callable (parameter `$callable`) and catches any `Throwable` matching the given base class (parameter `$throwableBaseClassName`, defaults to `Throwable`). Any other `Throwable` isn't caught. It then asserts that the `Throwable`'s class (optional parameter `$throwableClassName`, defaults to `Throwable`), message (optional parameter `$throwableMessage`, defaults to `null`) and code (optional parameter `$throwableCode`, defaults to `null`) match the expected, or throws a  `ExpectationFailedException` otherwise. The exception message can either be a string, requiring an exact match, or an arbitrary `Constraint` (e.g. `PHPUnit\\Framework\\Constraint\\StringContains`) to match the exception message. The constraint optionally requires an exact match of the class name (optional parameter `$throwableExactMatch`, defaults to `false`).\n\nThe `ThrowableAssertsTrait` trait exposes two public methods for the `CallableThrows` constraint: Use `ThrowableAssertsTrait::assertCallableThrows()` to perform an assertion, and `ThrowableAssertsTrait::callableThrows()` to create a new instance of the `CallableThrows` constraint.\n\n**Usage:**\n\n```php\n// using `PhrozenByte\\PHPUnitThrowableAsserts\\ThrowableAssertsTrait` trait\nThrowableAssertsTrait::assertCallableThrows(\n    callable $callable,                                // the Callable to call\n    string $throwableClassName = Throwable::class,     // assert that a Throwable of the given class is thrown\n    Constraint|string $throwableMessage = null,        // assert that its message matches the given constraint\n    int|string $throwableCode = null,                  // assert that its code matches the given one\n    bool $throwableExactMatch = false,                 // whether an exact match of the class name is required\n    string $throwableBaseClassName = Throwable::class, // catch all Throwables of the given class\n    string $message = ''                               // additional information about the test\n);\n\n// using new instance of `PhrozenByte\\PHPUnitThrowableAsserts\\Constraint\\CallableThrows`\nnew CallableThrows(\n    string $className = Throwable::class,\n    Constraint|string $message = null,\n    int|string $code = null,\n    bool $exactMatch = false,\n    string $baseClassName = Throwable::class\n);\n```\n\n**Example:**\n\n```php\n$controller = new BookController();\n$bookName = \"The Hitchhiker's Guide to the Galaxy\";\n$bookReleaseDate = '1979-10-12';\n\n$this-\u003eassertCallableThrows(\n    $this-\u003ecallableProxy([ $controller, 'create' ], $bookName, $bookReleaseDate),\n    BookAlreadyExistsException::class,\n    'Unable to create book: Book already exists'\n);\n```\n\n**Debugging:**\n\n```php\n$service = new HitchhikersGuideService();\n$towel = false;\n$answer = 42;\n\n$this-\u003eassertCallableThrows(\n    static function () use ($service, $towel, $answer) {\n        $service-\u003echeckAnswer($answer); // throws a OpaqueAnswerException\n        $service-\u003echeckTowel($towel);   // throws a PanicException (unreachable code)\n    },\n    PanicException::class,\n    'I forgot my towel'\n);\n\n// Will fail with the following message:\n//\n//     Failed asserting that {closure}() throws a PanicException whose message is 'Time to panic'.\n//     Encountered invalid OpaqueAnswerException: I do not understand.\n//     --- Expected\n//     +++ Actual\n//     @@ @@\n//     -'Time to panic'\n//     +'I do not understand'\n```\n\n### Constraint `CallableThrowsNot`\n\nThe [`CallableThrowsNot` constraint](https://github.com/PhrozenByte/phpunit-throwable-asserts/blob/master/src/Constraint/CallableThrowsNot.php) asserts that a Callable doesn't throw a specific `Throwable`. It can be used as a more specific alternative to PHPUnit's built-in `expectNotToPerformAssertions()` method.\n\nThis constraint calls the given Callable (parameter `$callable`) and catches any `Throwable` matching the given class (optional parameter `$throwableClassName`, defaults to `Throwable`), message (optional parameter `$throwableMessage`, defaults to `null`) and code (optional parameter `$throwableCode`, defaults to `null`). All conditions must match, otherwise the `Throwable` is re-thrown. The exception message can either be a string, requiring an exact match, or an arbitrary `Constraint` (e.g. `PHPUnit\\Framework\\Constraint\\StringContains`) to match the exception message. The constraint optionally requires an exact match of the class name (optional parameter `$throwableExactMatch`, defaults to `false`).\n\nThis is *not* the same as negating the `CallableThrows` constraint, which consumes all non-matching `Throwable`s and throws a `ExpectationFailedException` instead. `CallableThrowsNot` will rather re-throw any non-matching `Throwable`. A `ExpectationFailedException` is only thrown when the Callable throws a `Throwable` matching all given conditions.\n\nThe `ThrowableAssertsTrait` trait exposes two public methods for the `CallableThrowsNot` constraint: Use `ThrowableAssertsTrait::assertCallableThrowsNot()` to perform an assertion, and `ThrowableAssertsTrait::callableThrowsNot()` to create a new instance of the `CallableThrowsNot` constraint.\n\n**Usage:**\n\n```php\n// using `PhrozenByte\\PHPUnitThrowableAsserts\\ThrowableAssertsTrait` trait\nThrowableAssertsTrait::assertCallableThrowsNot(\n    callable $callable,                            // the Callable to call\n    string $throwableClassName = Throwable::class, // assert that no Throwable of the given class is thrown\n    Constraint|string $throwableMessage = null,    // catch Throwables matching the given message constraint only\n    int|string $throwableCode = null,              // catch Throwables matching the given code only\n    bool $throwableExactMatch = false,             // whether only Throwables of the given class are caught\n    string $message = ''                           // additional information about the test\n);\n\n// using new instance of `PhrozenByte\\PHPUnitThrowableAsserts\\Constraint\\CallableThrowsNot`\nnew CallableThrowsNot(\n    string $className = Throwable::class,\n    Constraint|string $message = null,\n    int|string $code = null,\n    bool $exactMatch = false\n);\n```\n\n**Example:**\n\n```php\n$controller = CharacterController();\n$character = 'Prostetnik Vogon Jeltz';\n\n$this-\u003eassertCallableThrowsNot(\n    $this-\u003ecallableProxy([ $controller, 'meet' ], $character),\n    VogonWantsToReadPoetException::class\n);\n```\n\n**Debugging:**\n\n```php\n$controller = new BookController();\n$bookName = \"The Hitchhiker's Guide to the Galaxy\";\n$bookReleaseDate = '1979-10-12';\n\n$this-\u003eassertCallableThrowsNot(\n    $this-\u003ecallableProxy([ $controller, 'create' ], $bookName, $bookReleaseDate),\n    BookAlreadyExistsException::class\n);\n\n// Will fail with the following message:\n//\n//     Failed asserting that BookController::create() does not throw a BookAlreadyExistsException\n//     Encountered invalid BookAlreadyExistsException: Unable to create book: Book already exists\n```\n\n### `CallableProxy` and `CachedCallableProxy`\n\n`PHPUnitThrowableAsserts` invokes Callables without arguments and discards a possible return value due to how PHPUnit evaluates values. One solution for this is to use anonymous functions with variable inheritance. As a neat alternative, `PHPUnitThrowableAsserts` provides the [`CallableProxy`](https://github.com/PhrozenByte/phpunit-throwable-asserts/blob/master/src/CallableProxy.php) and [`CachedCallableProxy`](https://github.com/PhrozenByte/phpunit-throwable-asserts/blob/master/src/CachedCallableProxy.php) helper classes.\n\nBoth helper classes receive the Callable to invoke (argument `$callable`), and the arguments to pass (any following argument, variadic `$arguments`) in their constructor. They furthermore implement PHPUnit's `PHPUnit\\Framework\\SelfDescribing` interface and the `toString()` method, improving error handling by allowing `PHPUnitThrowableAsserts` to better designate the called method. `CachedCallableProxy` additionally implements the `getReturnValue()` and `getThrowable()` methods. `getReturnValue()` returns the cached return value of the Callables last invocation, while `getThrowable()` returns a possibly thrown `Throwable`.\n\nThe `ThrowableAssertsTrait` trait exposes two public methods to create instances of `CallableProxy` and `CachedCallableProxy`: Use `ThrowableAssertsTrait::callableProxy()` to create a new instance of `CallableProxy`, or `ThrowableAssertsTrait::cachedCallableProxy()` to create a new instance of `CachedCallableProxy`.\n\n**Usage:**\n\n```php\n// create new instance of `PhrozenByte\\PHPUnitThrowableAsserts\\CallableProxy`\n// using the `PhrozenByte\\PHPUnitThrowableAsserts\\ThrowableAssertsTrait` trait\nThrowableAssertsTrait::callableProxy(\n     callable $callable,    // the Callable to invoke\n     mixed    ...$arguments // the arguments to pass to the Callable\n);\n\n// create new instance of `PhrozenByte\\PHPUnitThrowableAsserts\\CachedCallableProxy`\n// using the `PhrozenByte\\PHPUnitThrowableAsserts\\ThrowableAssertsTrait` trait\n$proxy = ThrowableAssertsTrait::cachedCallableProxy(\n     callable $callable,    // the Callable to invoke\n     mixed    ...$arguments // the arguments to pass to the Callable\n);\n\n// get return value of the Callable (`CachedCallableProxy` only)\n$proxy-\u003egetReturnValue();\n\n// get a possibly thrown Throwable (`CachedCallableProxy` only)\n$proxy-\u003egetThrowable();\n```\n\n**Example:**\n\n```php\n$computer = new DeepThought();\n$question = 'What is the Answer to the Ultimate Question of Life, the Universe, and Everything?';\n\n// using `PhrozenByte\\PHPUnitThrowableAsserts\\CallableProxy`\n// if the assertion fails, `ExpectationFailedException`'s message will point to DeepThought::ask() as source\n$askQuestion = $this-\u003ecachedCallableProxy([ $computer, 'ask' ], $question);\n$this-\u003eassertCallableThrowsNot($askQuestion);\n$answer = $askQuestion-\u003egetReturnValue();\n\n// using anonymous function\n// if the assertion fails, `ExpectationFailedException` will just name {closure} as source\n$answer = null;\n$this-\u003eassertCallableThrowsNot(static function () use ($computer, $question, \u0026$answer) {\n    // use variable reference to pass the return value\n    $answer = $computer-\u003eask($question);\n});\n```\n\n### PHP errors, warnings and notices\n\nPHPUnit converts [PHP errors](https://www.php.net/manual/en/function.error-reporting.php) (`E_RECOVERABLE_ERROR`), warnings (`E_WARNING` and `E_USER_WARNING`), notices (`E_NOTICE`, `E_USER_NOTICE` and `E_STRICT`), and deprecation notices (`E_DEPRECATED` and `E_USER_DEPRECATED`) to `PHPUnit\\Framework\\Error\\…` exceptions (`…\\Error`, `…\\Warning`, `…\\Notice` and `…\\Deprecated` respectively) by default. This allows you to use `PHPUnitThrowableAssertions`'s [`assertCallableThrows()`](#constraint-callablethrows) and [`assertCallableThrowsNot()`](#constraint-callablethrowsnot) assertions to also catch any PHP error; simply use one of the `PHPUnit\\Framework\\Error\\…` classes.\n\nPlease don't confuse PHP errors with PHP's [`Error` class](https://www.php.net/manual/de/class.error.php) introduced in PHP 7.0. The latter already is a `Throwable` and can be caught as usual.\n\n**Example:**\n\n```php\n$this-\u003eassertCallableThrows(\n    static function () {\n        // triggers a E_NOTICE PHP error\n        echo $undefinedVariable;\n    },\n    \\PHPUnit\\Framework\\Error\\Notice::class,\n    'Undefined variable: undefinedVariable'\n);\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphrozenbyte%2Fphpunit-throwable-asserts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphrozenbyte%2Fphpunit-throwable-asserts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphrozenbyte%2Fphpunit-throwable-asserts/lists"}