PHP code example of rybakit / phpunit-extras

1. Go to this page and download the library: Download rybakit/phpunit-extras library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

rybakit / phpunit-extras example snippets


use App\Tests\Attribute\RequiresMySqlServer;
use App\Tests\Attribute\Sql;
use PHPUnitExtras\TestCase;

#[RequiresMySqlServer('^5.6|^8.0')]
final class CacheableRepositoryTest extends TestCase
{
    #[Sql('DROP TABLE IF EXISTS %target_method%')]
    #[Sql('CREATE TABLE %target_method% (id INT UNSIGNED PRIMARY KEY)')]
    #[Sql('INSERT INTO %target_method% (id) VALUES (1)')]
    public function testFindByIdCachesResultSet() : void
    {
        $tableName = $this->resolvePlaceholders('%target_method%');
        $repository = $this->createRepository($tableName);

        $this->expectSelectStatementToBeExecutedOnce();

        $repository->findById(1);
        $repository->findById(1);
    }
}

use PHPUnitExtras\TestCase;

final class MyTest extends TestCase
{
    // ...
}

use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\Before;
use PHPUnitExtras\Attribute\Attributes;

final class MyTest extends TestCase
{
    use Attributes;

    #[Before]
    protected function processTestAttributesBeforeTest() : void
    {
        $this->processTestAttributes(static::class, $this->name());
    }

    // ...
}

#[RequiresIf('<condition>')]

use PHPUnitExtras\Attribute\RequiresIf;

#[RequiresIf('server.AWS_ACCESS_KEY_ID')]
#[RequiresIf('server.AWS_SECRET_ACCESS_KEY')]
final class AwsS3AdapterTest extends TestCase
{
    // ...
}

use PHPUnitExtras\Attribute\Requirement\IfRequirement;

// ...

$context = ['db' => $this->getDbConnection()];
$attributeProcessorBuilder->addRequirement(new IfRequirement($context));

namespace App\Tests;

use PHPUnitExtras\Attribute\ProcessableAttribute;
use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver;
use PHPUnitExtras\Attribute\Requirement\Requirement;
use PHPUnitExtras\Attribute\Target;

#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
final class RequiresFeature implements ProcessableAttribute
{
    public function __construct(public readonly string $feature)
    {
    }
}

final class FeatureRequirement implements Requirement
{
    public function getAttributeClass() : string
    {
        return RequiresFeature::class;
    }

    public function check(ProcessableAttribute $attribute, Target $target, PlaceholderResolver $placeholderResolver) : ?string
    {
        if (!$attribute instanceof RequiresFeature) {
            throw new \InvalidArgumentException('FeatureRequirement only handles RequiresFeature attributes');
        }

        $feature = $placeholderResolver->resolve($attribute->feature, $target);

        return FeatureFlags::isEnabled($feature)
            ? null
            : \sprintf('Feature "%s" is 

use PHPUnitExtras\Attribute\RequiresConstant;

#[RequiresConstant('Redis::SERIALIZER_MSGPACK')]
public function testSerializeToMessagePack() : void 
{
    // ...
}

use PHPUnitExtras\Attribute\RequiresPackage;

#[RequiresPackage('symfony/uid ^5.1')]
public function testUseUuidAsPrimaryKey() : void 
{
    // ...
}

namespace App\Tests;

#[Example('%target_class%')]
#[Example('%target_class_full%')]
final class FoobarTest extends TestCase
{
    // ...
}

#[Example('%target_method%')]
#[Example('%target_method_full%')]
public function testFoobar() : void 
{
    // ...
}

#[Log('%tmp_dir%/%target_class%.%target_method%.log testing Foobar')]
public function testFoobar() : void 
{
    // ...
}

namespace App\Tests\PhpUnit;

use PHPUnitExtras\Attribute\Processor\Processor;
use PHPUnitExtras\Attribute\ProcessableAttribute;
use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver;
use PHPUnitExtras\Attribute\Target;

final class SqlProcessor implements Processor
{
    private $conn;

    public function __construct(\PDO $conn)
    {
        $this->conn = $conn;
    }

    public function getAttributeClasses() : array
    {
        return [Sql::class];
    }

    public function process(ProcessableAttribute $attribute, Target $target, PlaceholderResolver $placeholderResolver) : void
    {
        \assert($attribute instanceof Sql);
        $sql = $placeholderResolver->resolve($attribute->sql, $target);
        $this->conn->exec($sql);
    }
}

namespace App\Tests\PhpUnit;

use PHPUnitExtras\Attribute\ProcessableAttribute;

#[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
final class Sql implements ProcessableAttribute
{
    public function __construct(public readonly string $sql)
    {
    }
}

namespace App\Tests\PhpUnit;

use PHPUnitExtras\Attribute\PlaceholderResolver\PlaceholderResolver;
use PHPUnitExtras\Attribute\Target;

final class TableNameResolver implements PlaceholderResolver
{
    public function getName() : string
    {
        return 'table_name';
    }

    /**
     * Replaces all occurrences of "%table_name%" with 
     * "table_<short-class-name>[_<short-method-name>]".
     */
    public function resolve(string $value, Target $target) : string
    {
        $tableName = 'table_'.$target->getClassShortName();
        if ($target->isOnMethod()) {
            $tableName .= '_'.$target->getMethodShortName();
        }

        return strtr($value, ['%table_name%' => $tableName]);
    }
}

namespace App\Tests;

use App\Tests\PhpUnit\SqlProcessor;
use App\Tests\PhpUnit\TableNameResolver;
use PHPUnitExtras\Attribute\AttributeProcessorBuilder;
use PHPUnitExtras\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    protected function createAttributeProcessorBuilder() : AttributeProcessorBuilder
    {
        return parent::createAttributeProcessorBuilder()
            ->addProcessor(new SqlProcessor($this->getConnection()))
            ->addPlaceholderResolver(new TableNameResolver());
    }

    protected function getConnection() : \PDO
    {
        // TODO: Implement getConnection() method.
    }
}

namespace App\Tests\PhpUnit;

use PHPUnitExtras\Attribute\AttributeExtension as BaseAttributeExtension;
use PHPUnitExtras\Attribute\AttributeProcessorBuilder;
use PHPUnit\Runner\Extension\Facade;
use PHPUnit\Runner\Extension\ParameterCollection;
use PHPUnit\TextUI\Configuration\Configuration;

class AttributeExtension extends BaseAttributeExtension
{
    private string $dsn = 'mysql:host=localhost;dbname=test';
    private ?\PDO $conn = null;

    public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters) : void
    {
        if ($parameters->has('dsn')) {
            $this->dsn = $parameters->get('dsn');
        }

        parent::bootstrap($configuration, $facade, $parameters);
    }

    protected function createAttributeProcessorBuilder() : AttributeProcessorBuilder
    {
        return parent::createAttributeProcessorBuilder()
            ->addProcessor(new SqlProcessor($this->getConnection()))
            ->addPlaceholderResolver(new TableNameResolver());
    }

    protected function getConnection() : \PDO
    {
        return $this->conn ?? $this->conn = new \PDO($this->dsn);
    }
}

namespace App\Tests\PhpUnit;

use PHPUnit\Framework\Assert;
use PHPUnitExtras\Expectation\Expectation;

final class FileCreatedExpectation implements Expectation
{
    private $filename;

    public function __construct(string $filename)
    {
        Assert::assertFileDoesNotExist($filename);
        $this->filename = $filename;
    }

    public function verify() : void
    {
        Assert::assertFileExists($this->filename);
    }
}

use PHPUnit\Framework\TestCase;
use PHPUnitExtras\Expectation\Expectations;

final class MyTest extends TestCase
{
    use Expectations;

    protected function tearDown() : void
    {
        $this->verifyExpectations();
    }

    // ...
}

public function testDumpPdfToFile() : void
{
    $filename = sprintf('%s/foobar.pdf', sys_get_temp_dir());

    $this->expect(new FileCreatedExpectation($filename));
    $this->generator->dump($filename);
}

namespace App\Tests\PhpUnit;

use PHPUnitExtras\Expectation\Expectation;

trait FileExpectations
{
    public function expectFileToBeCreated(string $filename) : void
    {
        $this->expect(new FileCreatedExpectation($filename));
    }

    // ...

    abstract protected function expect(Expectation $expectation) : void;
}

namespace App\Tests\PhpUnit;

use PHPUnitExtras\Expectation\ExpressionContext;

final class SelectStatementCountContext implements ExpressionContext
{
    private $conn;
    private $expression;
    private $initialValue;
    private $finalValue;

    private function __construct(\PDO $conn, string $expression)
    {
        $this->conn = $conn;
        $this->expression = $expression;
        $this->initialValue = $this->getValue();
    }

    public static function exactly(\PDO $conn, int $count) : self
    {
        return new self($conn, "new_count === old_count + $count");
    }

    public static function atLeast(\PDO $conn, int $count) : self
    {
        return new self($conn, "new_count >= old_count + $count");
    }

    public static function atMost(\PDO $conn, int $count) : self
    {
        return new self($conn, "new_count <= old_count + $count");
    }

    public function getExpression() : string
    {
        return $this->expression;
    }

    public function getValues() : array
    {
        if (null === $this->finalValue) {
            $this->finalValue = $this->getValue();
        }

        return [
            'old_count' => $this->initialValue,
            'new_count' => $this->finalValue,
        ];
    }

    private function getValue() : int
    {
        $stmt = $this->conn->query("SHOW GLOBAL STATUS LIKE 'Com_select'");
        $stmt->execute();

        return (int) $stmt->fetchColumn(1);
    }
}

namespace App\Tests\PhpUnit;

use PHPUnitExtras\Expectation\Expectation;
use PHPUnitExtras\Expectation\ExpressionExpectation;

trait SelectStatementExpectations
{
    public function expectSelectStatementToBeExecuted(int $count) : void
    {
        $context = SelectStatementCountContext::exactly($this->getConnection(), $count);
        $this->expect(new ExpressionExpectation($context));
    }

    public function expectSelectStatementToBeExecutedOnce() : void
    {
        $this->expectSelectStatementToBeExecuted(1);
    }

    // ...

    abstract protected function expect(Expectation $expectation) : void;
    abstract protected function getConnection() : \PDO;
}

use App\Tests\PhpUnit\SelectStatementExpectations;
use PHPUnitExtras\TestCase;

final class CacheableRepositoryTest extends TestCase
{
    use SelectStatementExpectations;

    public function testFindByIdCachesResultSet() : void
    {
        $repository = $this->createRepository();

        $this->expectSelectStatementToBeExecutedOnce();

        $repository->findById(1);
        $repository->findById(1);
    }

    // ...

    protected function getConnection() : \PDO
    {
        // TODO: Implement getConnection() method.
    }
}
xml
	<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	    xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
	    bootstrap="vendor/autoload.php"
	>
    <!-- ... -->

	    <extensions>
	        <bootstrap class="App\Tests\PhpUnit\AttributeExtension" />
	    </extensions>
	</phpunit>