Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TesterBundle] PHPUnit SetUpAutowire extension refactoring and support AutowireParameter #275

Merged
merged 1 commit into from
May 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/src/Repository/TagRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public function __construct(ManagerRegistry $registry)
parent::__construct($registry, Tag::class);
}

public function findActive()
public function findActive(): array
{
return $this->findBy(['active' => true]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,19 @@

namespace Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire;

use Draw\Bundle\TesterBundle\WebTestCase as DrawWebTestCase;
use Draw\Component\Core\Reflection\ReflectionAccessor;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as SymfonyWebTestCase;

#[\Attribute(\Attribute::TARGET_PROPERTY)]
class AutowireClient
class AutowireClient implements AutowireInterface
{
public static function getPriority(): int
{
return 1000;
}

public function __construct(
private array $options = [],
private array $server = [],
Expand All @@ -22,4 +32,34 @@ public function getServer(): array
{
return $this->server;
}

public function autowire(TestCase $testCase, \ReflectionProperty $reflectionProperty): void
{
if (!$testCase instanceof SymfonyWebTestCase && !$testCase instanceof DrawWebTestCase) {
throw new \RuntimeException(
sprintf(
'AutowireClient attribute can only be used in %s or %s.',
SymfonyWebTestCase::class,
DrawWebTestCase::class
)
);
}

// This is to ensure the kernel is not booted before calling createClient
// Can happen if we use the container in a setUpBeforeClass method or a beforeClass hook
ReflectionAccessor::callMethod(
$testCase,
'ensureKernelShutdown'
);

$reflectionProperty->setValue(
$testCase,
ReflectionAccessor::callMethod(
$testCase,
'createClient',
$this->getOptions(),
$this->getServer()
)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire;

use PHPUnit\Framework\TestCase;

interface AutowireInterface
{
public static function getPriority(): int;

public function autowire(TestCase $testCase, \ReflectionProperty $reflectionProperty);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,48 @@

namespace Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire;

use Draw\Component\Core\Reflection\ReflectionAccessor;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

#[\Attribute(\Attribute::TARGET_PROPERTY)]
class AutowireMock extends AutowireService
class AutowireMock implements AutowireInterface
{
public static function getPriority(): int
{
return 255;
}

public function autowire(TestCase $testCase, \ReflectionProperty $reflectionProperty): void
{
$propertyName = $reflectionProperty->getName();
$type = $reflectionProperty->getType();

if (!$type instanceof \ReflectionIntersectionType) {
throw new \RuntimeException('Property '.$propertyName.' of class '.$testCase::class.' must have a type hint intersection with Mock.');
}

$types = $type->getTypes();

if (2 !== \count($types)) {
throw new \RuntimeException('Property '.$propertyName.' of class '.$testCase::class.' can only have 2 intersection types.');
}

foreach ($types as $type) {
if (!$type instanceof \ReflectionNamedType) {
throw new \RuntimeException('Property '.$propertyName.' of class '.$testCase::class.' intersection must be of named type.');
}

if (MockObject::class === $type->getName()) {
continue;
}

$reflectionProperty->setValue(
$testCase,
ReflectionAccessor::callMethod($testCase, 'createMock', $type->getName())
);

return;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@

namespace Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire;

use Draw\Component\Core\Reflection\ReflectionAccessor;
use PHPUnit\Framework\TestCase;

#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
class AutowireMockProperty
class AutowireMockProperty implements AutowireInterface
{
public static function getPriority(): int
{
return -100;
}

public function __construct(private string $property, private ?string $fromProperty = null)
{
$this->fromProperty ??= $property;
Expand All @@ -19,4 +27,18 @@ public function getFromProperty(): string
{
return $this->fromProperty;
}

public function autowire(TestCase $testCase, \ReflectionProperty $reflectionProperty): void
{
$object = $reflectionProperty->getValue($testCase);

ReflectionAccessor::setPropertyValue(
$object,
$this->getProperty(),
ReflectionAccessor::getPropertyValue(
$testCase,
$this->getFromProperty()
)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire;

use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

#[\Attribute(\Attribute::TARGET_PROPERTY)]
class AutowireParameter implements AutowireInterface
{
public static function getPriority(): int
{
return 0;
}

public function __construct(private string $parameter)
{
}

public function getParameter(): string
{
return $this->parameter;
}

public function autowire(TestCase $testCase, \ReflectionProperty $reflectionProperty): void
{
\assert($testCase instanceof KernelTestCase);

$container = (new \ReflectionMethod($testCase, 'getContainer'))->invoke($testCase);

$reflectionProperty->setValue(
$testCase,
$container->get(ParameterBagInterface::class)->resolveValue($this->getParameter())
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@

namespace Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire;

use Draw\Component\Core\Reflection\ReflectionExtractor;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

#[\Attribute(\Attribute::TARGET_PROPERTY)]
class AutowireService
class AutowireService implements AutowireInterface
{
public static function getPriority(): int
{
return 0;
}

public function __construct(private ?string $serviceId = null)
{
}
Expand All @@ -13,4 +22,28 @@ public function getServiceId(): ?string
{
return $this->serviceId;
}

public function autowire(TestCase $testCase, \ReflectionProperty $reflectionProperty): void
{
\assert($testCase instanceof KernelTestCase);

$serviceId = $this->serviceId;

if (null === $serviceId) {
$classes = ReflectionExtractor::getClasses($reflectionProperty->getType());

if (1 !== \count($classes)) {
throw new \RuntimeException('Property '.$reflectionProperty->getName().' of class '.$testCase::class.' must have a type hint.');
}

$serviceId = $classes[0];
}

$container = (new \ReflectionMethod($testCase, 'getContainer'))->invoke($testCase);

$reflectionProperty->setValue(
$testCase,
$container->get($serviceId)
);
}
}
Loading
Loading