diff --git a/config/sets/phpunit100.php b/config/sets/phpunit100.php index c5417ad0..eb9045c4 100644 --- a/config/sets/phpunit100.php +++ b/config/sets/phpunit100.php @@ -4,6 +4,7 @@ use Rector\Config\RectorConfig; use Rector\PHPUnit\PHPUnit100\Rector\Class_\AddProphecyTraitRector; +use Rector\PHPUnit\PHPUnit100\Rector\Class_\ParentTestClassConstructorRector; use Rector\PHPUnit\PHPUnit100\Rector\Class_\PublicDataProviderClassMethodRector; use Rector\PHPUnit\PHPUnit100\Rector\Class_\StaticDataProviderClassMethodRector; use Rector\PHPUnit\PHPUnit100\Rector\MethodCall\PropertyExistsWithoutAssertRector; @@ -23,6 +24,7 @@ WithConsecutiveRector::class, RemoveSetMethodsMethodCallRector::class, PropertyExistsWithoutAssertRector::class, + ParentTestClassConstructorRector::class, ]); $rectorConfig->ruleWithConfiguration(RenameMethodRector::class, [ diff --git a/rules-tests/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector/Fixture/mocking_helper.php.inc b/rules-tests/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector/Fixture/mocking_helper.php.inc new file mode 100644 index 00000000..e1cff0cf --- /dev/null +++ b/rules-tests/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector/Fixture/mocking_helper.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector/Fixture/skip_abstract_classes.php.inc b/rules-tests/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector/Fixture/skip_abstract_classes.php.inc new file mode 100644 index 00000000..fd52e97c --- /dev/null +++ b/rules-tests/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector/Fixture/skip_abstract_classes.php.inc @@ -0,0 +1,9 @@ +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/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector/config/configured_rule.php b/rules-tests/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector/config/configured_rule.php new file mode 100644 index 00000000..ef03034e --- /dev/null +++ b/rules-tests/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(ParentTestClassConstructorRector::class); +}; diff --git a/rules/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector.php b/rules/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector.php new file mode 100644 index 00000000..528e727a --- /dev/null +++ b/rules/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector.php @@ -0,0 +1,124 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isInTestClass($node)) { + return null; + } + + if ($this->shouldSkipClass($node)) { + return null; + } + + // it already has a constructor, skip as it might require specific tweaking + if ($node->getMethod(MethodName::CONSTRUCT)) { + return null; + } + + $constructorClassMethod = new ClassMethod(MethodName::CONSTRUCT); + $constructorClassMethod->flags |= Modifiers::PUBLIC; + $constructorClassMethod->stmts[] = new Expression($this->createParentConstructorCall()); + + $node->stmts = array_merge([$constructorClassMethod], $node->stmts); + + return $node; + } + + private function createParentConstructorCall(): StaticCall + { + $staticClassConstFetch = new ClassConstFetch(new Name('static'), 'class'); + + return new StaticCall(new Name('parent'), MethodName::CONSTRUCT, [new Arg($staticClassConstFetch)]); + } + + private function shouldSkipClass(Class_ $class): bool + { + if ($class->isAbstract()) { + return true; + } + + if ($class->isAnonymous()) { + return true; + } + + $className = $this->getName($class); + + // loaded automatically by PHPUnit + if (str_ends_with((string) $className, 'Test')) { + return true; + } + + return str_ends_with((string) $className, 'TestCase'); + } +}