Skip to content

Commit

Permalink
[TesterBundle] PHPUnit SetUpAutowire extension refactoring and suppor…
Browse files Browse the repository at this point in the history
…t AutowireParameter
  • Loading branch information
mpoiriert committed May 26, 2024
1 parent b509ee6 commit a611e8a
Show file tree
Hide file tree
Showing 10 changed files with 298 additions and 189 deletions.
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

0 comments on commit a611e8a

Please sign in to comment.