eliashaeussler/phpunit-attributes

为PHPUnit测试提供额外的属性

1.2.1 2024-09-08 15:04 UTC

This package is auto-updated.

Last update: 2024-09-22 05:07:42 UTC


README

PHPUnit属性

Coverage Maintainability CGL Tests Supported PHP Versions

一个Composer库,提供了额外的属性来增强PHPUnit的测试。

🔥 安装

Packagist Packagist Downloads

composer require --dev eliashaeussler/phpunit-attributes

⚡ 使用

该库附带了一个可以直接使用的PHPUnit扩展。必须在您的PHPUnit配置文件中注册

 <?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>

一些属性可以通过自定义扩展参数进行配置。这些必须添加到扩展注册部分,如下所示

     <extensions>
-        <bootstrap class="EliasHaeussler\PHPUnitAttributes\PHPUnitAttributesExtension" />
+        <bootstrap class="EliasHaeussler\PHPUnitAttributes\PHPUnitAttributesExtension">
+            <parameter name="fancyParameterName" value="fancyParameterValue" />
+        </bootstrap>
     </extensions>

🎢 属性

以下属性包含在这个库中

#[RequiresClass]

范围:类与方法级别

使用此属性,可以将测试或测试用例标记为仅在存在特定类时执行。给定的类必须能够被当前类加载器(通常是Composer的默认类加载器)加载。

配置

默认情况下,需要不存在类的测试用例将被跳过。但是,可以通过使用handleMissingClasses扩展参数来配置此行为。如果设置为fail,则缺少类的测试用例将失败(默认为skip

<extensions>
    <bootstrap class="EliasHaeussler\PHPUnitAttributes\PHPUnitAttributesExtension">
        <parameter name="handleMissingClasses" value="fail" />
    </bootstrap>
</extensions>

示例

final class DummyTest extends TestCase
{
    #[RequiresClass(AnImportantClass::class)]
    public function testDummyAction(): void
    {
        // ...
    }
}
更多示例

需要单个类

类级别

#[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.
    }
}

方法级别

final class DummyTest extends TestCase
{
    #[RequiresClass(AnImportantClass::class)]
    public function testDummyAction(): void
    {
        // Skipped if AnImportantClass is missing.
    }

    public function testOtherDummyAction(): void
    {
        // Not skipped.
    }
}

需要单个类并提供自定义消息

类级别

#[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.
    }
}

方法级别

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.
    }
}

需要单个类并定义自定义结果行为

类级别

#[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.
    }
}

方法级别

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.
    }
}

需要多个类

类级别

#[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.
    }
}

方法级别

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]

范围:类与方法级别

此属性可用于为单个测试以及完整的测试类定义特定的包要求。所需的包应通过Composer安装。您可以可选地定义版本约束和自定义消息。

重要

此属性确定已安装的Composer包来自Composer构建时生成的InstalledVersions类。为了正确读取此类,必须在您的PHPUnit引导脚本中包含Composer生成的自动加载器

<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
>
    <!-- ... -->
</phpunit>

您还可以将脚本作为命令选项传递:phpunit --bootstrap vendor/autoload.php

配置

默认情况下,不满足要求的测试用例将被跳过。但是,可以通过使用handleUnsatisfiedPackageRequirements扩展参数来配置此行为。如果设置为fail,则不满足要求的测试用例将失败(默认为skip

<extensions>
    <bootstrap class="EliasHaeussler\PHPUnitAttributes\PHPUnitAttributesExtension">
        <parameter name="handleUnsatisfiedPackageRequirements" value="fail" />
    </bootstrap>
</extensions>

示例

final class DummyTest extends TestCase
{
    #[RequiresPackage('symfony/console')]
    public function testDummyAction(): void
    {
        // ...
    }
}
更多示例

需要显式的Composer包

类级别

#[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.
    }
}

方法级别

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.
    }
}

需要匹配给定模式的任何Composer包

类级别

#[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.
    }
}

方法级别

final class DummyTest extends TestCase
{
    #[RequiresPackage('symfony/*')]
    public function testDummyAction(): void
    {
        // Skipped if no symfony/* packages are installed.
    }

    public function testOtherDummyAction(): void
    {
        // Not skipped.
    }
}

需要具有给定版本约束的Composer包

类级别

#[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.
    }
}

方法级别

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.
    }
}

需要Composer包并提供自定义消息

类级别

#[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.
    }
}

方法级别

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.
    }
}

需要Composer包并定义自定义结果行为

类级别

#[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.
    }
}

方法级别

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.
    }
}

多个要求

类级别

#[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.
    }
}

方法级别

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.md

⭐ 许可证

本项目采用GNU通用公共许可证3.0(或更新版)授权。