Skip to content

Commit

Permalink
[TesterBundle] Add AutowireClient attribute (#271)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Martin Poirier Théorêt <[email protected]>
  • Loading branch information
DumitracheAdrian and mpoiriert authored May 21, 2024
1 parent 753929f commit 2d330bf
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,35 @@

namespace Draw\Bundle\TesterBundle\PHPStan\Rules\Properties;

use Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire\AutowireService;
use PHPStan\Reflection\PropertyReflection;
use PHPStan\Rules\Properties\ReadWritePropertiesExtension;

class AutowireReadWritePropertiesExtension implements ReadWritePropertiesExtension
{
public function __construct(
private string $attribute
) {
}

public function isAlwaysRead(PropertyReflection $property, string $propertyName): bool
{
return false;
}

public function isAlwaysWritten(PropertyReflection $property, string $propertyName): bool
{
return $this->hasProperAttribute($property, $propertyName, AutowireService::class);
return $this->hasProperAttribute($property, $propertyName);
}

public function isInitialized(PropertyReflection $property, string $propertyName): bool
{
return $this->hasProperAttribute($property, $propertyName, AutowireService::class);
return $this->hasProperAttribute($property, $propertyName);
}

private function hasProperAttribute(PropertyReflection $property, string $propertyName, string $attribute): bool
private function hasProperAttribute(PropertyReflection $property, string $propertyName): bool
{
$properReflection = $property->getDeclaringClass()->getNativeProperty($propertyName)->getNativeReflection();

return 0 !== \count($properReflection->getAttributes($attribute, \ReflectionAttribute::IS_INSTANCEOF));
return 0 !== \count($properReflection->getAttributes($this->attribute, \ReflectionAttribute::IS_INSTANCEOF));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

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

#[\Attribute(\Attribute::TARGET_PROPERTY)]
class AutowireClient
{
public function __construct(
private array $options = [],
private array $server = [],
) {
}

public function getOptions(): array
{
return $this->options;
}

public function getServer(): array
{
return $this->server;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

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

use Draw\Bundle\TesterBundle\WebTestCase as DrawWebTestCase;
use Draw\Component\Core\Reflection\ReflectionAccessor;
use Draw\Component\Core\Reflection\ReflectionExtractor;
use PHPUnit\Event\Code\TestMethod;
Expand All @@ -14,6 +15,7 @@
use PHPUnit\Runner\Extension\ParameterCollection;
use PHPUnit\TextUI\Configuration\Configuration;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as SymfonyWebTestCase;

class SetUpAutowireExtension implements Extension
{
Expand Down Expand Up @@ -58,6 +60,8 @@ public function notify(TestPrepared $event): void
return;
}

$this->initializeClients($testCase);

$container = null;

foreach ($this->getPropertyAttributes($testCase) as [$property, $serviceId]) {
Expand Down Expand Up @@ -96,6 +100,64 @@ public function notify(TestPrepared $event): void
}
}

private function initializeClients(TestCase $testCase): void
{
$clientAttributes = iterator_to_array($this->getClientAttributes($testCase));

if (empty($clientAttributes)) {
return;
}

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'
);

foreach ($clientAttributes as [$property, $attribute]) {
\assert($property instanceof \ReflectionProperty);
\assert($attribute instanceof \ReflectionAttribute);

$autoWireClient = $attribute->newInstance();
\assert($autoWireClient instanceof AutowireClient);

$property->setValue(
$testCase,
ReflectionAccessor::callMethod(
$testCase,
'createClient',
$autoWireClient->getOptions(),
$autoWireClient->getServer()
)
);
}
}

private function getClientAttributes(TestCase $testCase): iterable
{
foreach ((new \ReflectionObject($testCase))->getProperties() as $property) {
$attribute = $property->getAttributes(
AutowireClient::class,
\ReflectionAttribute::IS_INSTANCEOF
)[0] ?? null;

if (null !== $attribute) {
yield [$property, $attribute];
}
}
}

/**
* @return iterable<array{\ReflectionProperty, string|AutowireMock}>
*/
Expand Down
4 changes: 4 additions & 0 deletions packages/tester-bundle/WebTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
namespace Draw\Bundle\TesterBundle;

use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\DomCrawlerAssertionsTrait;
use Symfony\Bundle\FrameworkBundle\Test\HttpClientAssertionsTrait;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;

abstract class WebTestCase extends KernelTestCase
{
use DomCrawlerAssertionsTrait;
use HttpClientAssertionsTrait;
use JsonResponseAssertionsTrait;

protected function tearDown(): void
Expand Down
11 changes: 10 additions & 1 deletion packages/tester-bundle/extension.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
services:
draw.tester_bundle.autowire_read_write_properties_extension:
draw.tester_bundle.autowire_client_read_write_properties_extension:
class: Draw\Bundle\TesterBundle\PHPStan\Rules\Properties\AutowireReadWritePropertiesExtension
arguments:
- 'Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire\AutowireClient'
tags:
- phpstan.properties.readWriteExtension

draw.tester_bundle.autowire_service_read_write_properties_extension:
class: Draw\Bundle\TesterBundle\PHPStan\Rules\Properties\AutowireReadWritePropertiesExtension
arguments:
- 'Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire\AutowireService'
tags:
- phpstan.properties.readWriteExtension
10 changes: 8 additions & 2 deletions tests/Controller/Api/TestDocumentControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@

use App\Message\NewTestDocumentMessage;
use Draw\Bundle\TesterBundle\Messenger\MessengerTesterTrait;
use Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire\AutowireClient;
use Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire\AutowiredInterface;
use Draw\Bundle\TesterBundle\WebTestCase;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;

class TestDocumentControllerTest extends WebTestCase
class TestDocumentControllerTest extends WebTestCase implements AutowiredInterface
{
use MessengerTesterTrait;

#[AutowireClient]
private KernelBrowser $client;

public function testCreate(): void
{
static::createClient()
$this->client
->jsonRequest(
'POST',
'/api/test-documents',
Expand Down
12 changes: 8 additions & 4 deletions tests/Controller/OpenApiControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

namespace App\Tests\Controller;

use Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire\AutowireClient;
use Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire\AutowiredInterface;
use Draw\Bundle\TesterBundle\WebTestCase;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;

class OpenApiControllerTest extends WebTestCase
class OpenApiControllerTest extends WebTestCase implements AutowiredInterface
{
#[AutowireClient]
private KernelBrowser $client;

private bool $writeFile = false;

public function testApiDocScopeAll(): void
Expand All @@ -22,9 +28,7 @@ public function assertApiDocIsTheSame(string $scope): void
{
$file = __DIR__.sprintf('/fixtures/api-doc-scope-%s.json', $scope);

$client = static::createClient();

$client->request('get', '/api-doc.json?scope='.$scope);
$this->client->request('get', '/api-doc.json?scope='.$scope);

static::assertResponseIsSuccessful();
static::assertResponseIsJson();
Expand Down
16 changes: 9 additions & 7 deletions tests/Controller/PingActionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@

namespace App\Tests\Controller;

use Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire\AutowireClient;
use Draw\Bundle\TesterBundle\PHPUnit\Extension\SetUpAutowire\AutowiredInterface;
use Draw\Bundle\TesterBundle\WebTestCase;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Component\HttpFoundation\Response;

class PingActionTest extends WebTestCase
class PingActionTest extends WebTestCase implements AutowiredInterface
{
#[AutowireClient]
private KernelBrowser $client;

public function testPing(): void
{
$client = static::createClient();

$client->request('GET', '/ping');
$this->client->request('GET', '/ping');

static::assertResponseStatusCodeSame(207);

Expand All @@ -36,9 +40,7 @@ public static function provideTestPingWithContext(): iterable
*/
public function testPingWithContext(string $context, int $statusCode): void
{
$client = static::createClient();

$client->request('GET', '/ping/'.$context);
$this->client->request('GET', '/ping/'.$context);

static::assertResponseStatusCodeSame($statusCode);

Expand Down
Loading

0 comments on commit 2d330bf

Please sign in to comment.