eliashaeussler / phpunit-attributes
Provides additional attributes for tests with PHPUnit
Installs: 8 023
Dependents: 3
Suggesters: 0
Security: 0
Stars: 2
Watchers: 1
Forks: 0
Open Issues: 4
Requires
- php: ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0
- phpunit/phpunit: ^10.4 || ^11.0
Requires (Dev)
- armin/editorconfig-cli: ^1.8 || ^2.0
- eliashaeussler/php-cs-fixer-config: ^2.0
- eliashaeussler/phpstan-config: ^2.5.1
- eliashaeussler/rector-config: ^3.0
- ergebnis/composer-normalize: ^2.30
- phpstan/extension-installer: ^1.2
- phpstan/phpstan-phpunit: ^1.1
- phpunit/phpcov: ^9.0 || ^10.0
- phpunit/phpunit: ^10.5.35 || ^11.4
- symfony/console: ^6.4 || ^7.0
This package is auto-updated.
Last update: 2024-11-29 04:56:37 UTC
README
PHPUnit Attributes
A Composer library with additional attributes to enhance testing with PHPUnit.
🔥 Installation
composer require --dev eliashaeussler/phpunit-attributes
⚡ Usage
The library ships with a ready-to-use PHPUnit extension. It must be registered in your PHPUnit configuration file:
<?xml version="1.0" encoding="UTF-8"?> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" bootstrap="vendor/autoload.php" > + <extensions> + <bootstrap class="EliasHaeussler\PHPUnitAttributes\PHPUnitAttributesExtension" /> + </extensions> <testsuites> <testsuite name="unit"> <directory>tests</directory> </testsuite> </testsuites> <source> <include> <directory>src</directory> </include> </source> </phpunit>
Some attributes can be configured with custom extension parameters. These must be added to the extension registration section like follows:
<extensions> - <bootstrap class="EliasHaeussler\PHPUnitAttributes\PHPUnitAttributesExtension" /> + <bootstrap class="EliasHaeussler\PHPUnitAttributes\PHPUnitAttributesExtension"> + <parameter name="fancyParameterName" value="fancyParameterValue" /> + </bootstrap> </extensions>
🎢 Attributes
The following attributes are shipped with this library:
#[RequiresClass]
Scope: Class & Method level
With this attribute, tests or test cases can be marked as to be only executed if a certain class exists. The given class must be loadable by the current class loader (which normally is Composer's default class loader).
Configuration
By default, test cases requiring non-existent classes are skipped. However, this
behavior can be configured by using the handleMissingClasses
extension parameter.
If set to fail
, test cases with missing classes will fail (defaults to skip
):
<extensions> <bootstrap class="EliasHaeussler\PHPUnitAttributes\PHPUnitAttributesExtension"> <parameter name="handleMissingClasses" value="fail" /> </bootstrap> </extensions>
Example
final class DummyTest extends TestCase { #[RequiresClass(AnImportantClass::class)] public function testDummyAction(): void { // ... } }
More examples
Require single class
Class level:
#[RequiresClass(AnImportantClass::class)] final class DummyTest extends TestCase { public function testDummyAction(): void { // Skipped if AnImportantClass is missing. } public function testOtherDummyAction(): void { // Skipped if AnImportantClass is missing. } }
Method level:
final class DummyTest extends TestCase { #[RequiresClass(AnImportantClass::class)] public function testDummyAction(): void { // Skipped if AnImportantClass is missing. } public function testOtherDummyAction(): void { // Not skipped. } }
Require single class and provide custom message
Class level:
#[RequiresClass(AnImportantClass::class, 'This test requires the `AnImportantClass` class.')] final class DummyTest extends TestCase { public function testDummyAction(): void { // Skipped if AnImportantClass is missing, along with custom message. } public function testOtherDummyAction(): void { // Skipped if AnImportantClass is missing, along with custom message. } }
Method level:
final class DummyTest extends TestCase { #[RequiresClass(AnImportantClass::class, 'This test requires the `AnImportantClass` class.')] public function testDummyAction(): void { // Skipped if AnImportantClass is missing, along with custom message. } public function testOtherDummyAction(): void { // Not skipped. } }
Require single class and define custom outcome behavior
Class level:
#[RequiresClass(AnImportantClass::class, outcomeBehavior: OutcomeBehavior::Fail)] final class DummyTest extends TestCase { public function testDummyAction(): void { // Fails if AnImportantClass is missing. } public function testOtherDummyAction(): void { // Fails if AnImportantClass is missing. } }
Method level:
final class DummyTest extends TestCase { #[RequiresClass(AnImportantClass::class, outcomeBehavior: OutcomeBehavior::Fail)] public function testDummyAction(): void { // Fails if AnImportantClass is missing. } public function testOtherDummyAction(): void { // Does not fail. } }
Require multiple classes
Class level:
#[RequiresClass(AnImportantClass::class)] #[RequiresClass(AnotherVeryImportantClass::class)] final class DummyTest extends TestCase { public function testDummyAction(): void { // Skipped if AnImportantClass and/or AnotherVeryImportantClass are missing. } public function testOtherDummyAction(): void { // Skipped if AnImportantClass and/or AnotherVeryImportantClass are missing. } }
Method level:
final class DummyTest extends TestCase { #[RequiresClass(AnImportantClass::class)] #[RequiresClass(AnotherVeryImportantClass::class)] public function testDummyAction(): void { // Skipped if AnImportantClass and/or AnotherVeryImportantClass are missing. } public function testOtherDummyAction(): void { // Not skipped. } }
#[RequiresPackage]
Scope: Class & Method level
This attribute can be used to define specific package requirements for single tests as well as complete test classes. A required package is expected to be installed via Composer. You can optionally define a version constraint and a custom message.
Important
The attribute determines installed Composer packages from the build-time
generated InstalledVersions
class built by Composer. In order to properly
read from this class , it's essential to include Composer's generated
autoloader in your PHPUnit bootstrap script:
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" bootstrap="vendor/autoload.php" > <!-- ... --> </phpunit>
You can also pass the script as command option: phpunit --bootstrap vendor/autoload.php
Configuration
By default, test cases with unsatisfied requirements are skipped. However, this
behavior can be configured by using the handleUnsatisfiedPackageRequirements
extension parameter. If set to fail
, test cases with unsatisfied requirements
will fail (defaults to skip
):
<extensions> <bootstrap class="EliasHaeussler\PHPUnitAttributes\PHPUnitAttributesExtension"> <parameter name="handleUnsatisfiedPackageRequirements" value="fail" /> </bootstrap> </extensions>
Example
final class DummyTest extends TestCase { #[RequiresPackage('symfony/console')] public function testDummyAction(): void { // ... } }
More examples
Require explicit Composer package
Class level:
#[RequiresPackage('symfony/console')] final class DummyTest extends TestCase { public function testDummyAction(): void { // Skipped if symfony/console is not installed. } public function testOtherDummyAction(): void { // Skipped if symfony/console is not installed. } }
Method level:
final class DummyTest extends TestCase { #[RequiresPackage('symfony/console')] public function testDummyAction(): void { // Skipped if symfony/console is not installed. } public function testOtherDummyAction(): void { // Not skipped. } }
Require any Composer package matching a given pattern
Class level:
#[RequiresPackage('symfony/*')] final class DummyTest extends TestCase { public function testDummyAction(): void { // Skipped if no symfony/* packages are installed. } public function testOtherDummyAction(): void { // Skipped if no symfony/* packages are installed. } }
Method level:
final class DummyTest extends TestCase { #[RequiresPackage('symfony/*')] public function testDummyAction(): void { // Skipped if no symfony/* packages are installed. } public function testOtherDummyAction(): void { // Not skipped. } }
Require Composer package with given version constraint
Class level:
#[RequiresPackage('symfony/console', '>= 7')] final class DummyTest extends TestCase { public function testDummyAction(): void { // Skipped if installed version of symfony/console is < 7. } public function testOtherDummyAction(): void { // Skipped if installed version of symfony/console is < 7. } }
Method level:
final class DummyTest extends TestCase { #[RequiresPackage('symfony/console', '>= 7')] public function testDummyAction(): void { // Skipped if installed version of symfony/console is < 7. } public function testOtherDummyAction(): void { // Not skipped. } }
Require Composer package and provide custom message
Class level:
#[RequiresPackage('symfony/console', message: 'This test requires the Symfony Console.')] final class DummyTest extends TestCase { public function testDummyAction(): void { // Skipped if symfony/console is not installed, along with custom message. } public function testOtherDummyAction(): void { // Skipped if symfony/console is not installed, along with custom message. } }
Method level:
final class DummyTest extends TestCase { #[RequiresPackage('symfony/console', message: 'This test requires the Symfony Console.')] public function testDummyAction(): void { // Skipped if symfony/console is not installed, along with custom message. } public function testOtherDummyAction(): void { // Not skipped. } }
Require Composer package and define custom outcome behavior
Class level:
#[RequiresPackage('symfony/console', outcomeBehavior: OutcomeBehavior::Fail)] final class DummyTest extends TestCase { public function testDummyAction(): void { // Fails if symfony/console is not installed. } public function testOtherDummyAction(): void { // Fails if symfony/console is not installed. } }
Method level:
final class DummyTest extends TestCase { #[RequiresPackage('symfony/console', outcomeBehavior: OutcomeBehavior::Fail)] public function testDummyAction(): void { // Fails if symfony/console is not installed. } public function testOtherDummyAction(): void { // Does not fail. } }
Multiple requirements
Class level:
#[RequiresPackage('symfony/console')] #[RequiresPackage('guzzlehttp/guzzle')] final class DummyTest extends TestCase { public function testDummyAction(): void { // Skipped if symfony/console and/or guzzlehttp/guzzle are not installed. } public function testOtherDummyAction(): void { // Skipped if symfony/console and/or guzzlehttp/guzzle are not installed. } }
Method level:
final class DummyTest extends TestCase { #[RequiresPackage('symfony/console')] #[RequiresPackage('guzzlehttp/guzzle')] public function testDummyAction(): void { // Skipped if symfony/console and/or guzzlehttp/guzzle are not installed. } public function testOtherDummyAction(): void { // Not skipped. } }
🧑💻 Contributing
Please have a look at CONTRIBUTING.md
.
⭐ License
This project is licensed under GNU General Public License 3.0 (or later).