This commit is contained in:
Tomas Votruba 2021-01-26 10:50:32 +01:00 committed by GitHub
parent 6ff43c3d7a
commit 485dce60bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1 additions and 1519 deletions

View File

@ -165,7 +165,6 @@
"Rector\\StaticTypeMapper\\": "packages/static-type-mapper/src",
"Rector\\CodeQualityStrict\\": "rules/code-quality-strict/src",
"Rector\\SymfonyCodeQuality\\": "rules/symfony-code-quality/src",
"Rector\\SymfonyPHPUnit\\": "rules/symfony-phpunit/src",
"Rector\\SymfonyPhpConfig\\": "rules/symfony-php-config/src",
"Rector\\Symfony\\": "rules/symfony/src",
"Rector\\Symfony2\\": "rules/symfony2/src",
@ -283,7 +282,6 @@
"Rector\\Sensio\\Tests\\": "rules/sensio/tests",
"Rector\\CodeQualityStrict\\Tests\\": "rules/code-quality-strict/tests",
"Rector\\SymfonyCodeQuality\\Tests\\": "rules/symfony-code-quality/tests",
"Rector\\SymfonyPHPUnit\\Tests\\": "rules/symfony-phpunit/tests",
"Rector\\SymfonyPhpConfig\\Tests\\": "rules/symfony-php-config/tests",
"Rector\\Symfony\\Tests\\": "rules/symfony/tests",
"Rector\\Symfony2\\Tests\\": "rules/symfony2/tests",

View File

@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
use Rector\Renaming\Rector\Name\RenameClassRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(RenameClassRector::class)
->call('configure', [[
RenameClassRector::OLD_TO_NEW_CLASSES => [
'CI_Controller' => 'CodeIgniter\Controller',
'CI_Model' => 'CodeIgniter\Model',
],
]]);
};

View File

@ -1,15 +0,0 @@
<?php
declare(strict_types=1);
use Rector\Generic\Rector\Property\InjectAnnotationClassRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(InjectAnnotationClassRector::class)
->call('configure', [[
InjectAnnotationClassRector::ANNOTATION_CLASSES => ['JMS\DiExtraBundle\Annotation\Inject'],
]]);
};

View File

@ -1,19 +0,0 @@
<?php
declare(strict_types=1);
use Rector\Renaming\Rector\Name\RenameClassRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(RenameClassRector::class)
->call('configure', [[
RenameClassRector::OLD_TO_NEW_CLASSES => [
'PHP_CodeSniffer_Sniffs_Sniff' => 'PHP_CodeSniffer\Sniffs\Sniff',
'PHP_CodeSniffer_File' => 'PHP_CodeSniffer\Files\File',
'PHP_CodeSniffer_Tokens' => 'PHP_CodeSniffer\Util\Tokens',
],
]]);
};

View File

@ -1,15 +0,0 @@
<?php
declare(strict_types=1);
use Rector\Generic\Rector\Property\InjectAnnotationClassRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(InjectAnnotationClassRector::class)
->call('configure', [[
InjectAnnotationClassRector::ANNOTATION_CLASSES => ['DI\Annotation\Inject'],
]]);
};

View File

@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
use Rector\PHPUnit\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(SelfContainerGetMethodCallFromTestToInjectPropertyRector::class);
};

View File

@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
use Rector\SymfonyPHPUnit\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(SelfContainerGetMethodCallFromTestToSetUpMethodRector::class);
};

View File

@ -25,7 +25,7 @@ final class PackageNamesProviderTest extends AbstractKernelTestCase
{
$packageNames = $this->packageNamesProvider->provide();
$packageNameCount = count($packageNames);
$this->assertGreaterThan(70, $packageNameCount);
$this->assertGreaterThan(60, $packageNameCount);
$this->assertContains('DeadCode', $packageNames);
$this->assertContains('Symfony5', $packageNames);

View File

@ -66,11 +66,6 @@ final class SetList
*/
public const CAKEPHP_FLUENT_OPTIONS = __DIR__ . '/../../../../config/set/cakephp-fluent-options.php';
/**
* @var string
*/
public const CODEIGNITER_40 = __DIR__ . '/../../../../config/set/codeigniter-40.php';
/**
* @var string
*/
@ -206,11 +201,6 @@ final class SetList
*/
public const GMAGICK_TO_IMAGICK = __DIR__ . '/../../../../config/set/gmagick_to_imagick.php';
/**
* @var string
*/
public const JMS_DECOUPLE = __DIR__ . '/../../../../config/set/jms-decouple.php';
/**
* @var string
*/
@ -466,11 +456,6 @@ final class SetList
*/
public const PHPUNIT_EXCEPTION = __DIR__ . '/../../../../config/set/phpunit-exception.php';
/**
* @var string
*/
public const PHPUNIT_INJECTOR = __DIR__ . '/../../../../config/set/phpunit-injector.php';
/**
* @var string
*/
@ -541,16 +526,6 @@ final class SetList
*/
public const PHP_80 = __DIR__ . '/../../../../config/set/php80.php';
/**
* @var string
*/
public const PHP_CODE_SNIFFER_30 = __DIR__ . '/../../../../config/set/php-code-sniffer30.php';
/**
* @var string
*/
public const PHP_DI_DECOUPLE = __DIR__ . '/../../../../config/set/php-di-decouple.php';
/**
* @var string
*/
@ -656,11 +631,6 @@ final class SetList
*/
public const SYMFONY_CONSTRUCTOR_INJECTION = __DIR__ . '/../../../../config/set/symfony-constructor-injection.php';
/**
* @var string
*/
public const SYMFONY_PHPUNIT = __DIR__ . '/../../../../config/set/symfony-phpunit.php';
/**
* @var string
*/

View File

@ -1,28 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\PHPUnit\Collector;
final class FormerVariablesByMethodCollector
{
/**
* @var array<string, array<string, string>>
*/
private $variablesByMethod = [];
public function addMethodVariable(string $method, string $variable, string $type): void
{
$this->variablesByMethod[$method][$variable] = $type;
}
public function reset(): void
{
$this->variablesByMethod = [];
}
public function getTypeByVariableByMethod(string $methodName, string $variableName): ?string
{
return $this->variablesByMethod[$methodName][$variableName] ?? null;
}
}

View File

@ -1,162 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\PHPUnit\Manipulator;
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Class_;
use Rector\Core\PhpParser\Node\Value\ValueResolver;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPUnit\Collector\FormerVariablesByMethodCollector;
use Rector\PostRector\Collector\NodesToRemoveCollector;
use Rector\SymfonyPHPUnit\Naming\ServiceNaming;
use Rector\SymfonyPHPUnit\Node\KernelTestCaseNodeAnalyzer;
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
final class OnContainerGetCallManipulator
{
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
/**
* @var SimpleCallableNodeTraverser
*/
private $simpleCallableNodeTraverser;
/**
* @var ServiceNaming
*/
private $serviceNaming;
/**
* @var KernelTestCaseNodeAnalyzer
*/
private $kernelTestCaseNodeAnalyzer;
/**
* @var ValueResolver
*/
private $valueResolver;
/**
* @var NodesToRemoveCollector
*/
private $nodesToRemoveCollector;
/**
* @var FormerVariablesByMethodCollector
*/
private $formerVariablesByMethodCollector;
public function __construct(
SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
KernelTestCaseNodeAnalyzer $kernelTestCaseNodeAnalyzer,
NodeNameResolver $nodeNameResolver,
NodesToRemoveCollector $nodesToRemoveCollector,
ServiceNaming $serviceNaming,
ValueResolver $valueResolver,
FormerVariablesByMethodCollector $formerVariablesByMethodCollector
) {
$this->nodeNameResolver = $nodeNameResolver;
$this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser;
$this->serviceNaming = $serviceNaming;
$this->kernelTestCaseNodeAnalyzer = $kernelTestCaseNodeAnalyzer;
$this->valueResolver = $valueResolver;
$this->nodesToRemoveCollector = $nodesToRemoveCollector;
$this->formerVariablesByMethodCollector = $formerVariablesByMethodCollector;
}
/**
* E.g. $someService
* $this->someService
*/
public function replaceFormerVariablesWithPropertyFetch(Class_ $class): void
{
$this->simpleCallableNodeTraverser->traverseNodesWithCallable(
$class->stmts,
function (Node $node): ?PropertyFetch {
if (! $node instanceof Variable) {
return null;
}
$variableName = $this->nodeNameResolver->getName($node);
if ($variableName === null) {
return null;
}
/** @var string|null $methodName */
$methodName = $node->getAttribute(AttributeKey::METHOD_NAME);
if ($methodName === null) {
return null;
}
$serviceType = $this->formerVariablesByMethodCollector->getTypeByVariableByMethod(
$methodName,
$variableName
);
if ($serviceType === null) {
return null;
}
$propertyName = $this->serviceNaming->resolvePropertyNameFromServiceType($serviceType);
return new PropertyFetch(new Variable('this'), $propertyName);
}
);
}
public function removeAndCollectFormerAssignedVariables(Class_ $class, bool $skipSetUpMethod = true): void
{
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($class->stmts, function (Node $node) use (
$skipSetUpMethod
): ?PropertyFetch {
if (! $node instanceof MethodCall) {
return null;
}
if ($skipSetUpMethod && $this->kernelTestCaseNodeAnalyzer->isSetUpOrEmptyMethod($node)) {
return null;
}
if (! $this->kernelTestCaseNodeAnalyzer->isOnContainerGetMethodCall($node)) {
return null;
}
$type = $this->valueResolver->getValue($node->args[0]->value);
if ($type === null) {
return null;
}
$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
if ($parentNode instanceof Assign) {
$this->processAssign($node, $parentNode, $type);
return null;
}
$propertyName = $this->serviceNaming->resolvePropertyNameFromServiceType($type);
return new PropertyFetch(new Variable('this'), $propertyName);
});
}
private function processAssign(MethodCall $methodCall, Assign $assign, string $type): void
{
$variableName = $this->nodeNameResolver->getName($assign->var);
if ($variableName === null) {
return;
}
/** @var string $methodName */
$methodName = $methodCall->getAttribute(AttributeKey::METHOD_NAME);
$this->formerVariablesByMethodCollector->addMethodVariable($methodName, $variableName, $type);
$this->nodesToRemoveCollector->addNodeToRemove($assign);
}
}

View File

@ -1,176 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\PHPUnit\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use Rector\Core\PhpParser\Node\Manipulator\ClassManipulator;
use Rector\Core\Rector\AbstractPHPUnitRector;
use Rector\PHPUnit\Collector\FormerVariablesByMethodCollector;
use Rector\PHPUnit\Manipulator\OnContainerGetCallManipulator;
use Rector\SymfonyPHPUnit\Node\KernelTestCaseNodeFactory;
use Rector\SymfonyPHPUnit\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector;
use Rector\SymfonyPHPUnit\SelfContainerMethodCallCollector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* Inspiration
* @see SelfContainerGetMethodCallFromTestToSetUpMethodRector
*
* @see https://github.com/shopsys/shopsys/pull/1392
* @see https://github.com/jakzal/phpunit-injector
*
* @see \Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\SelfContainerGetMethodCallFromTestToInjectPropertyRectorTest
*/
final class SelfContainerGetMethodCallFromTestToInjectPropertyRector extends AbstractPHPUnitRector
{
/**
* @var SelfContainerMethodCallCollector
*/
private $selfContainerMethodCallCollector;
/**
* @var KernelTestCaseNodeFactory
*/
private $kernelTestCaseNodeFactory;
/**
* @var OnContainerGetCallManipulator
*/
private $onContainerGetCallManipulator;
/**
* @var ClassManipulator
*/
private $classManipulator;
/**
* @var FormerVariablesByMethodCollector
*/
private $formerVariablesByMethodCollector;
public function __construct(
ClassManipulator $classManipulator,
KernelTestCaseNodeFactory $kernelTestCaseNodeFactory,
OnContainerGetCallManipulator $onContainerGetCallManipulator,
SelfContainerMethodCallCollector $selfContainerMethodCallCollector,
FormerVariablesByMethodCollector $formerVariablesByMethodCollector
) {
$this->selfContainerMethodCallCollector = $selfContainerMethodCallCollector;
$this->kernelTestCaseNodeFactory = $kernelTestCaseNodeFactory;
$this->onContainerGetCallManipulator = $onContainerGetCallManipulator;
$this->classManipulator = $classManipulator;
$this->formerVariablesByMethodCollector = $formerVariablesByMethodCollector;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Change $container->get() calls in PHPUnit to @inject properties autowired by jakzal/phpunit-injector',
[
new CodeSample(
<<<'CODE_SAMPLE'
use PHPUnit\Framework\TestCase;
class SomeClassTest extends TestCase {
public function test()
{
$someService = $this->getContainer()->get(SomeService::class);
}
}
class SomeService
{
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
use PHPUnit\Framework\TestCase;
class SomeClassTest extends TestCase {
/**
* @var SomeService
* @inject
*/
private $someService;
public function test()
{
$someService = $this->someService;
}
}
class SomeService
{
}
CODE_SAMPLE
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Class_::class];
}
/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
if (! $this->isInTestClass($node)) {
return null;
}
// 1. find $this->getService(x)
$serviceTypes = $this->selfContainerMethodCallCollector->collectContainerGetServiceTypes($node, false);
if ($serviceTypes === []) {
return null;
}
// 2. - add @inject to existing properties of that type, to prevent re-adding them
foreach ($serviceTypes as $key => $serviceType) {
$existingProperty = $this->classManipulator->findPropertyByType($node, $serviceType);
if ($existingProperty !== null) {
$this->addInjectAnnotationToProperty($existingProperty);
unset($serviceTypes[$key]);
}
}
// 3. create private properties with this types
$privateProperties = $this->kernelTestCaseNodeFactory->createPrivatePropertiesFromTypes($node, $serviceTypes);
$this->addInjectAnnotationToProperties($privateProperties);
$node->stmts = array_merge($privateProperties, $node->stmts);
// 4. remove old in-method $property assigns
$this->formerVariablesByMethodCollector->reset();
$this->onContainerGetCallManipulator->removeAndCollectFormerAssignedVariables($node, false);
// 4. replace former variables by $this->someProperty
$this->onContainerGetCallManipulator->replaceFormerVariablesWithPropertyFetch($node);
return $node;
}
private function addInjectAnnotationToProperty(Property $property): void
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$phpDocInfo->addBareTag('@inject');
}
/**
* @param Property[] $properties
*/
private function addInjectAnnotationToProperties(array $properties): void
{
foreach ($properties as $property) {
$this->addInjectAnnotationToProperty($property);
}
}
}

View File

@ -1,38 +0,0 @@
<?php
namespace Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Fixture;
use Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Source\TestCaseWithGetContainer;
class AnotherCaseTest extends TestCaseWithGetContainer
{
protected function getDomainBaseUrl(): string
{
/** @var \Shopsys\FrameworkBundle\Component\Domain\Domain $domain */
$domain = $this->getContainer()->get(Domain::class);
return $domain->getUrl();
}
}
?>
-----
<?php
namespace Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Fixture;
use Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Source\TestCaseWithGetContainer;
class AnotherCaseTest extends TestCaseWithGetContainer
{
/**
* @inject
*/
private \Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Fixture\Domain $domain;
protected function getDomainBaseUrl(): string
{
return $this->domain->getUrl();
}
}
?>

View File

@ -1,51 +0,0 @@
<?php
namespace Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Fixture;
use Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Source\TestCaseWithGetContainer;
class SomeClassTest extends TestCaseWithGetContainer
{
public function test()
{
$someService = $this->getContainer()->get(SomeService::class);
$someService->someMethod();
}
public function nested()
{
$someService = $this->getContainer()->get(SomeService::class);
$someService->anotherMethod();
}
}
class SomeService { }
?>
-----
<?php
namespace Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Fixture;
use Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Source\TestCaseWithGetContainer;
class SomeClassTest extends TestCaseWithGetContainer
{
/**
* @inject
*/
private \Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Fixture\SomeService $someService;
public function test()
{
$this->someService->someMethod();
}
public function nested()
{
$this->someService->anotherMethod();
}
}
class SomeService { }
?>

View File

@ -1,53 +0,0 @@
<?php
namespace Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Fixture;
use Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Source\RandomElasticClient;
use Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Source\TestCaseWithGetContainer;
class FromSetUpToInject extends TestCaseWithGetContainer
{
/**
* @var RandomElasticClient
*/
private $elasticsearchClient;
protected function setUp(): void
{
$this->elasticsearchClient = $this->getContainer()->get(RandomElasticClient::class);
}
public function testSomething()
{
$this->elasticsearchClient->init();
}
}
?>
-----
<?php
namespace Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Fixture;
use Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Source\RandomElasticClient;
use Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Source\TestCaseWithGetContainer;
class FromSetUpToInject extends TestCaseWithGetContainer
{
/**
* @var RandomElasticClient
* @inject
*/
private $elasticsearchClient;
protected function setUp(): void
{
}
public function testSomething()
{
$this->elasticsearchClient->init();
}
}
?>

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector;
use Iterator;
use Rector\PHPUnit\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class SelfContainerGetMethodCallFromTestToInjectPropertyRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return SelfContainerGetMethodCallFromTestToInjectPropertyRector::class;
}
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Source;
final class RandomElasticClient
{
}

View File

@ -1,15 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\PHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToInjectPropertyRector\Source;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerInterface;
abstract class TestCaseWithGetContainer extends TestCase
{
public function getContainer(): ContainerInterface
{
}
}

View File

@ -1,17 +0,0 @@
<?php
declare(strict_types=1);
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->defaults()
->public()
->autowire()
->autoconfigure();
$services->load('Rector\SymfonyPHPUnit\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Rector']);
};

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\SymfonyPHPUnit\Naming;
use Nette\Utils\Strings;
use PHPStan\Type\ObjectType;
use Rector\Naming\Naming\PropertyNaming;
final class ServiceNaming
{
/**
* @var PropertyNaming
*/
private $propertyNaming;
public function __construct(PropertyNaming $propertyNaming)
{
$this->propertyNaming = $propertyNaming;
}
public function resolvePropertyNameFromServiceType(string $serviceType): string
{
if (Strings::contains($serviceType, '_') && ! Strings::contains($serviceType, '\\')) {
return $this->propertyNaming->underscoreToName($serviceType);
}
return $this->propertyNaming->fqnToVariableName(new ObjectType($serviceType));
}
}

View File

@ -1,66 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\SymfonyPHPUnit\Node;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use Rector\Core\ValueObject\MethodName;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
final class KernelTestCaseNodeAnalyzer
{
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
/**
* @var NodeTypeResolver
*/
private $nodeTypeResolver;
public function __construct(NodeNameResolver $nodeNameResolver, NodeTypeResolver $nodeTypeResolver)
{
$this->nodeNameResolver = $nodeNameResolver;
$this->nodeTypeResolver = $nodeTypeResolver;
}
public function isOnContainerGetMethodCall(Node $node): bool
{
return $this->isSelfContainerGetMethodCall($node);
}
/**
* Is inside setUp() class method
*/
public function isSetUpOrEmptyMethod(Node $node): bool
{
$methodName = $node->getAttribute(AttributeKey::METHOD_NAME);
return $methodName === MethodName::SET_UP || $methodName === null;
}
/**
* Matches:
* $this->getService()
*/
private function isSelfContainerGetMethodCall(Node $node): bool
{
if (! $node instanceof MethodCall) {
return false;
}
if (! $this->nodeNameResolver->isName($node->name, 'get')) {
return false;
}
return $this->nodeTypeResolver->isObjectType(
$node->var,
'Symfony\Component\DependencyInjection\ContainerInterface'
);
}
}

View File

@ -1,154 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\SymfonyPHPUnit\Node;
use Nette\Utils\Strings;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\ObjectType;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\Core\ValueObject\MethodName;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpSpecToPHPUnit\PHPUnitTypeDeclarationDecorator;
use Rector\SymfonyPHPUnit\Naming\ServiceNaming;
use Symplify\Astral\ValueObject\NodeBuilder\MethodBuilder;
final class KernelTestCaseNodeFactory
{
/**
* @var PHPUnitTypeDeclarationDecorator
*/
private $phpUnitTypeDeclarationDecorator;
/**
* @var NodeFactory
*/
private $nodeFactory;
/**
* @var ServiceNaming
*/
private $serviceNaming;
public function __construct(
NodeFactory $nodeFactory,
PHPUnitTypeDeclarationDecorator $phpUnitTypeDeclarationDecorator,
ServiceNaming $serviceNaming
) {
$this->phpUnitTypeDeclarationDecorator = $phpUnitTypeDeclarationDecorator;
$this->nodeFactory = $nodeFactory;
$this->serviceNaming = $serviceNaming;
}
/**
* @param string[] $serviceTypes
*/
public function createSetUpClassMethodWithGetTypes(Class_ $class, array $serviceTypes): ?ClassMethod
{
$assigns = $this->createSelfContainerGetWithTypeAssigns($class, $serviceTypes);
if ($assigns === []) {
return null;
}
$stmts = array_merge([new StaticCall(new Name('parent'), MethodName::SET_UP)], $assigns);
$classMethodBuilder = new MethodBuilder(MethodName::SET_UP);
$classMethodBuilder->makeProtected();
$classMethodBuilder->addStmts($stmts);
$classMethod = $classMethodBuilder->getNode();
$this->phpUnitTypeDeclarationDecorator->decorate($classMethod);
return $classMethod;
}
/**
* @param string[] $serviceTypes
*
* @return Property[]
*/
public function createPrivatePropertiesFromTypes(Class_ $class, array $serviceTypes): array
{
$properties = [];
/** @var string $className */
$className = $class->getAttribute(AttributeKey::CLASS_NAME);
foreach ($serviceTypes as $serviceType) {
$propertyName = $this->serviceNaming->resolvePropertyNameFromServiceType($serviceType);
// skip existing properties
if (property_exists($className, $propertyName)) {
continue;
}
$serviceType = new ObjectType($serviceType);
$properties[] = $this->nodeFactory->createPrivatePropertyFromNameAndType($propertyName, $serviceType);
}
return $properties;
}
/**
* @param string[] $serviceTypes
*
* @return Expression[]
*
* E.g. "['SomeService']" "$this->someService = $this->getService(SomeService::class);"
*/
public function createSelfContainerGetWithTypeAssigns(Class_ $class, array $serviceTypes): array
{
$stmts = [];
/** @var string $className */
$className = $class->getAttribute(AttributeKey::CLASS_NAME);
foreach ($serviceTypes as $serviceType) {
$propertyName = $this->serviceNaming->resolvePropertyNameFromServiceType($serviceType);
// skip existing properties
if (property_exists($className, $propertyName)) {
continue;
}
$propertyFetch = new PropertyFetch(new Variable('this'), $propertyName);
$methodCall = $this->createSelfContainerGetWithTypeMethodCall($serviceType);
$assign = new Assign($propertyFetch, $methodCall);
$stmts[] = new Expression($assign);
}
return $stmts;
}
private function createSelfContainerGetWithTypeMethodCall(string $serviceType): MethodCall
{
$staticPropertyFetch = new StaticPropertyFetch(new Name('self'), 'container');
$methodCall = new MethodCall($staticPropertyFetch, 'get');
if (Strings::contains($serviceType, '_') && ! Strings::contains($serviceType, '\\')) {
// keep string
$getArgumentValue = new String_($serviceType);
} else {
$getArgumentValue = new ClassConstFetch(new FullyQualified($serviceType), 'class');
}
$methodCall->args[] = new Arg($getArgumentValue);
return $methodCall;
}
}

View File

@ -1,157 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\SymfonyPHPUnit\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Core\Rector\AbstractPHPUnitRector;
use Rector\Core\ValueObject\MethodName;
use Rector\PHPUnit\Manipulator\OnContainerGetCallManipulator;
use Rector\SymfonyPHPUnit\Node\KernelTestCaseNodeFactory;
use Rector\SymfonyPHPUnit\SelfContainerMethodCallCollector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\SelfContainerGetMethodCallFromTestToSetUpMethodRectorTest
*/
final class SelfContainerGetMethodCallFromTestToSetUpMethodRector extends AbstractPHPUnitRector
{
/**
* @var KernelTestCaseNodeFactory
*/
private $kernelTestCaseNodeFactory;
/**
* @var SelfContainerMethodCallCollector
*/
private $selfContainerMethodCallCollector;
/**
* @var OnContainerGetCallManipulator
*/
private $onContainerGetCallManipulator;
public function __construct(
KernelTestCaseNodeFactory $kernelTestCaseNodeFactory,
OnContainerGetCallManipulator $onContainerGetCallManipulator,
SelfContainerMethodCallCollector $selfContainerMethodCallCollector
) {
$this->kernelTestCaseNodeFactory = $kernelTestCaseNodeFactory;
$this->selfContainerMethodCallCollector = $selfContainerMethodCallCollector;
$this->onContainerGetCallManipulator = $onContainerGetCallManipulator;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Move self::$container service fetching from test methods up to setUp method',
[
new CodeSample(
<<<'CODE_SAMPLE'
use ItemRepository;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class SomeTest extends KernelTestCase
{
public function testOne()
{
$itemRepository = $this->getService(ItemRepository::class);
$itemRepository->doStuff();
}
public function testTwo()
{
$itemRepository = $this->getService(ItemRepository::class);
$itemRepository->doAnotherStuff();
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
use ItemRepository;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class SomeTest extends KernelTestCase
{
/**
* @var \ItemRepository
*/
private $itemRepository;
protected function setUp()
{
parent::setUp();
$this->itemRepository = $this->getService(ItemRepository::class);
}
public function testOne()
{
$this->itemRepository->doStuff();
}
public function testTwo()
{
$this->itemRepository->doAnotherStuff();
}
}
CODE_SAMPLE
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Class_::class];
}
/**
* @param Class_ $node
*/
public function refactor(Node $node): ?Node
{
if (! $this->isInTestClass($node)) {
return null;
}
// 1. find $this->getService(<X>)
$serviceTypes = $this->selfContainerMethodCallCollector->collectContainerGetServiceTypes($node);
if ($serviceTypes === []) {
return null;
}
// 2. put them to setUp() method
$setUpClassMethod = $node->getMethod(MethodName::SET_UP);
if (! $setUpClassMethod instanceof ClassMethod) {
$setUpClassMethod = $this->kernelTestCaseNodeFactory->createSetUpClassMethodWithGetTypes(
$node,
$serviceTypes
);
if ($setUpClassMethod !== null) {
$node->stmts = array_merge([$setUpClassMethod], $node->stmts);
}
} else {
$assigns = $this->kernelTestCaseNodeFactory->createSelfContainerGetWithTypeAssigns($node, $serviceTypes);
$setUpClassMethod->stmts = array_merge((array) $setUpClassMethod->stmts, $assigns);
}
// 3. create private properties with this types
$privateProperties = $this->kernelTestCaseNodeFactory->createPrivatePropertiesFromTypes($node, $serviceTypes);
$node->stmts = array_merge($privateProperties, $node->stmts);
// 4. remove old in-method $property assigns
$this->onContainerGetCallManipulator->removeAndCollectFormerAssignedVariables($node);
// 5. replace former variables by $this->someProperty
$this->onContainerGetCallManipulator->replaceFormerVariablesWithPropertyFetch($node);
return $node;
}
}

View File

@ -1,90 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\SymfonyPHPUnit;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use Rector\Core\PhpParser\Node\Value\ValueResolver;
use Rector\SymfonyPHPUnit\Node\KernelTestCaseNodeAnalyzer;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
final class SelfContainerMethodCallCollector
{
/**
* @var KernelTestCaseNodeAnalyzer
*/
private $kernelTestCaseNodeAnalyzer;
/**
* @var SimpleCallableNodeTraverser
*/
private $simpleCallableNodeTraverser;
/**
* @var ValueResolver
*/
private $valueResolver;
public function __construct(
SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
KernelTestCaseNodeAnalyzer $kernelTestCaseNodeAnalyzer,
ValueResolver $valueResolver
) {
$this->kernelTestCaseNodeAnalyzer = $kernelTestCaseNodeAnalyzer;
$this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser;
$this->valueResolver = $valueResolver;
}
/**
* @return string[]
*/
public function collectContainerGetServiceTypes(Class_ $class, bool $skipSetUpMethod = true): array
{
$serviceTypes = [];
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($class->stmts, function (Node $node) use (
&$serviceTypes,
$skipSetUpMethod
): ?Node {
if (! $node instanceof MethodCall) {
return null;
}
if (! $this->kernelTestCaseNodeAnalyzer->isOnContainerGetMethodCall($node)) {
return null;
}
if ($skipSetUpMethod && $this->kernelTestCaseNodeAnalyzer->isSetUpOrEmptyMethod($node)) {
return null;
}
/** @var MethodCall $node */
$serviceType = $this->valueResolver->getValue($node->args[0]->value);
if ($serviceType === null) {
return null;
}
if (! is_string($serviceType)) {
return null;
}
if ($this->shouldSkipServiceType($serviceType)) {
return null;
}
$serviceTypes[] = $serviceType;
return null;
});
return array_unique($serviceTypes);
}
private function shouldSkipServiceType(string $serviceType): bool
{
return $serviceType === SessionInterface::class;
}
}

View File

@ -1,57 +0,0 @@
<?php
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Fixture;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository;
class ExistingSetUpTest extends KernelTestCase
{
protected function setUp(): void
{
$value = 5;
}
public function testOne()
{
$itemRepository = self::$container->get(ItemRepository::class);
$itemRepository->doStuff();
}
public function testTwo()
{
$itemRepository = self::$container->get(ItemRepository::class);
$itemRepository->doAnotherStuff();
}
}
?>
-----
<?php
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Fixture;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository;
class ExistingSetUpTest extends KernelTestCase
{
private \Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository $itemRepository;
protected function setUp(): void
{
$value = 5;
$this->itemRepository = self::$container->get(\Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository::class);
}
public function testOne()
{
$this->itemRepository->doStuff();
}
public function testTwo()
{
$this->itemRepository->doAnotherStuff();
}
}
?>

View File

@ -1,47 +0,0 @@
<?php
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Fixture;
use ItemRepository;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ParentClassWithPropertyKernelTestCase;
class ExtendsParentClassWithProperty extends ParentClassWithPropertyKernelTestCase
{
public function testOne()
{
$itemRepository = self::$container->get(ItemRepository::class);
$itemRepository->doStuff();
}
public function testTwo()
{
$itemRepository = self::$container->get(ItemRepository::class);
$itemRepository->doAnotherStuff();
}
}
?>
-----
<?php
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Fixture;
use ItemRepository;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ParentClassWithPropertyKernelTestCase;
class ExtendsParentClassWithProperty extends ParentClassWithPropertyKernelTestCase
{
public function testOne()
{
$this->itemRepository->doStuff();
}
public function testTwo()
{
$this->itemRepository->doAnotherStuff();
}
}
?>

View File

@ -1,51 +0,0 @@
<?php
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Fixture;
use Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class SomeTest extends KernelTestCase
{
public function testOne()
{
$itemRepository = self::$container->get(ItemRepository::class);
$itemRepository->doStuff();
}
public function testTwo()
{
$itemRepository = self::$container->get(ItemRepository::class);
$itemRepository->doAnotherStuff();
}
}
?>
-----
<?php
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Fixture;
use Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class SomeTest extends KernelTestCase
{
private \Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository $itemRepository;
protected function setUp()
{
parent::setUp();
$this->itemRepository = self::$container->get(\Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository::class);
}
public function testOne()
{
$this->itemRepository->doStuff();
}
public function testTwo()
{
$this->itemRepository->doAnotherStuff();
}
}
?>

View File

@ -1,49 +0,0 @@
<?php
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Fixture;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository;
class InstantCall extends KernelTestCase
{
public function testOne()
{
$item = self::$container->get(ItemRepository::class)->get(1);
}
public function testTwo()
{
$item = self::$container->get(ItemRepository::class)->get(2);
}
}
?>
-----
<?php
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Fixture;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository;
class InstantCall extends KernelTestCase
{
private \Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository $itemRepository;
protected function setUp()
{
parent::setUp();
$this->itemRepository = self::$container->get(\Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ItemRepository::class);
}
public function testOne()
{
$item = $this->itemRepository->get(1);
}
public function testTwo()
{
$item = $this->itemRepository->get(2);
}
}
?>

View File

@ -1,21 +0,0 @@
<?php
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Fixture;
use Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source\ParentClassWithPropertyKernelTestCase;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class SkipSessions extends ParentClassWithPropertyKernelTestCase
{
public function testOne()
{
$firstSession = self::$container->get(SessionInterface::class);
$firstSession->doStuff();
}
public function testTwo()
{
$secondSession = self::$container->get(SessionInterface::class);
$secondSession->doAnotherStuff();
}
}

View File

@ -1,38 +0,0 @@
<?php
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Fixture;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class StringServiceName extends KernelTestCase
{
public function testOne()
{
$someValue = self::$container->get('some_value');
$someValue->doStuff();
}
}
?>
-----
<?php
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Fixture;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class StringServiceName extends KernelTestCase
{
private \some_value $someValue;
protected function setUp()
{
parent::setUp();
$this->someValue = self::$container->get('some_value');
}
public function testOne()
{
$this->someValue->doStuff();
}
}
?>

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector;
use Iterator;
use Rector\SymfonyPHPUnit\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class SelfContainerGetMethodCallFromTestToSetUpMethodRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return SelfContainerGetMethodCallFromTestToSetUpMethodRector::class;
}
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source;
final class ItemRepository
{
}

View File

@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\SymfonyPHPUnit\Tests\Rector\Class_\SelfContainerGetMethodCallFromTestToSetUpMethodRector\Source;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
abstract class ParentClassWithPropertyKernelTestCase extends KernelTestCase
{
protected $itemRepository;
}