From 8acf739422b816414861bfbf66314f78130bf0f1 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 30 Jul 2024 15:39:49 +0200 Subject: [PATCH] Add SetUpBeforeClassToSetUpRector --- docs/rector_rules_overview.md | 33 +++- .../Fixture/keep_unrelated_method.php.inc | 21 +++ .../Fixture/skip_other_property.php.inc | 16 ++ .../Fixture/some_setup_before_class.php.inc | 47 +++++ .../SetUpBeforeClassToSetUpRectorTest.php | 28 +++ .../Source/SomeService.php | 8 + .../config/configured_rule.php | 10 + .../Class_/AddCoversClassAttributeRector.php | 2 +- .../Class_/SetUpBeforeClassToSetUpRector.php | 174 ++++++++++++++++++ 9 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Fixture/keep_unrelated_method.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Fixture/skip_other_property.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Fixture/some_setup_before_class.php.inc create mode 100644 rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/SetUpBeforeClassToSetUpRectorTest.php create mode 100644 rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Source/SomeService.php create mode 100644 rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/config/configured_rule.php create mode 100644 rules/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector.php diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md index 64e457a3..2a26270f 100644 --- a/docs/rector_rules_overview.md +++ b/docs/rector_rules_overview.md @@ -1,4 +1,4 @@ -# 54 Rules Overview +# 55 Rules Overview ## AddCoversClassAttributeRector @@ -912,6 +912,37 @@ Replace `@test` with prefixed function
+## SetUpBeforeClassToSetUpRector + +Change `setUpBeforeClass()` to `setUp()` if not needed + +- class: [`Rector\PHPUnit\CodeQuality\Rector\Class_\SetUpBeforeClassToSetUpRector`](../rules/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector.php) + +```diff + use PHPUnit\Framework\TestCase; + + final class SomeTest extends TestCase + { +- private static $someService; ++ private $someService; + +- public static function setUpBeforeClass(): void ++ protected function setUp(): void + { +- self::$someService = new SomeService(); ++ $this->someService = new SomeService(); + } + + public function test() + { +- $result = self::$someService->getValue(); ++ $result = $this->someService->getValue(); + } + } +``` + +
+ ## SimplifyForeachInstanceOfRector Simplify unnecessary foreach check of instances diff --git a/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Fixture/keep_unrelated_method.php.inc b/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Fixture/keep_unrelated_method.php.inc new file mode 100644 index 00000000..5fba966f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Fixture/keep_unrelated_method.php.inc @@ -0,0 +1,21 @@ +getValue(); + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Fixture/skip_other_property.php.inc b/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Fixture/skip_other_property.php.inc new file mode 100644 index 00000000..ff9e353b --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Fixture/skip_other_property.php.inc @@ -0,0 +1,16 @@ +getValue(); + } +} + +?> +----- +someService = new SomeService(); + } + + public function test() + { + $result = $this->someService->getValue(); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/SetUpBeforeClassToSetUpRectorTest.php b/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/SetUpBeforeClassToSetUpRectorTest.php new file mode 100644 index 00000000..71da6af2 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/SetUpBeforeClassToSetUpRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Source/SomeService.php b/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Source/SomeService.php new file mode 100644 index 00000000..3bb92a9f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector/Source/SomeService.php @@ -0,0 +1,8 @@ +rule(SetUpBeforeClassToSetUpRector::class); +}; diff --git a/rules/CodeQuality/Rector/Class_/AddCoversClassAttributeRector.php b/rules/CodeQuality/Rector/Class_/AddCoversClassAttributeRector.php index b3096aba..e3622eeb 100644 --- a/rules/CodeQuality/Rector/Class_/AddCoversClassAttributeRector.php +++ b/rules/CodeQuality/Rector/Class_/AddCoversClassAttributeRector.php @@ -102,7 +102,7 @@ public function refactor(Node $node): ?Node $possibleTestClassNames = $this->resolveSourceClassNames($className); $matchingTestClassName = $this->matchExistingClassName($possibleTestClassNames); - if ($matchingTestClassName === null) { + if (! is_string($matchingTestClassName)) { return null; } diff --git a/rules/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector.php b/rules/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector.php new file mode 100644 index 00000000..bba3be3f --- /dev/null +++ b/rules/CodeQuality/Rector/Class_/SetUpBeforeClassToSetUpRector.php @@ -0,0 +1,174 @@ +getValue(); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use PHPUnit\Framework\TestCase; + +final class SomeTest extends TestCase +{ + private $someService; + + protected function setUp(): void + { + $this->someService = new SomeService(); + } + + public function test() + { + $result = $this->someService->getValue(); + } +} +CODE_SAMPLE + , + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + $className = $this->getName($node); + if ($className === null) { + return null; + } + + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + $setUpBeforeClassMethod = $node->getMethod(MethodName::SET_UP_BEFORE_CLASS); + if (! $setUpBeforeClassMethod instanceof ClassMethod) { + return null; + } + + $changedPropertyNames = []; + + // replace static property fetches + $this->traverseNodesWithCallable($setUpBeforeClassMethod, function (Node $node) use ( + &$changedPropertyNames + ) { + if (! $node instanceof Assign) { + return null; + } + + if (! $node->var instanceof StaticPropertyFetch) { + return null; + } + + $staticPropertyFetch = $node->var; + if (! $this->isName($staticPropertyFetch->class, 'self')) { + return null; + } + + $node->var = new PropertyFetch(new Variable('this'), $staticPropertyFetch->name); + $propertyName = $this->getName($staticPropertyFetch->name); + if (! is_string($propertyName)) { + return null; + } + + $changedPropertyNames[] = $propertyName; + }); + + if ($changedPropertyNames === []) { + return null; + } + + // remove static flag + $setUpBeforeClassMethod->flags -= Class_::MODIFIER_STATIC; + + // remove public flag + $setUpBeforeClassMethod->flags -= Class_::MODIFIER_PUBLIC; + + // make protected + $setUpBeforeClassMethod->flags += Class_::MODIFIER_PROTECTED; + + $setUpBeforeClassMethod->name = new Identifier('setUp'); + + foreach ($node->getProperties() as $property) { + if (! $property->isStatic()) { + continue; + } + + if ($this->isNames($property, $changedPropertyNames)) { + $property->flags -= Class_::MODIFIER_STATIC; + } + } + + // replace same property access in the class + $this->traverseNodesWithCallable($node->getMethods(), function (Node $node) use ( + $changedPropertyNames + ): ?PropertyFetch { + if (! $node instanceof StaticPropertyFetch) { + return null; + } + + if (! $this->isNames($node, $changedPropertyNames)) { + return null; + } + + return new PropertyFetch(new Variable('this'), $node->name); + }); + + return $node; + } +}