Skip to content

Commit 6ce3cda

Browse files
committed
[phpunit 12] Add AllowMockObjectsWhereParentClassRector
1 parent 0a04c39 commit 6ce3cda

File tree

7 files changed

+207
-2
lines changed

7 files changed

+207
-2
lines changed

config/sets/phpunit120.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use Rector\Config\RectorConfig;
66
use Rector\PHPUnit\PHPUnit120\Rector\CallLike\CreateStubOverCreateMockArgRector;
7+
use Rector\PHPUnit\PHPUnit120\Rector\Class_\AllowMockObjectsWhereParentClassRector;
8+
use Rector\PHPUnit\PHPUnit120\Rector\Class_\AllowMockObjectsWithoutExpectationsAttributeRector;
79
use Rector\PHPUnit\PHPUnit120\Rector\Class_\AssertIsTypeMethodCallRector;
810
use Rector\PHPUnit\PHPUnit120\Rector\Class_\PropertyCreateMockToCreateStubRector;
911
use Rector\PHPUnit\PHPUnit120\Rector\Class_\RemoveOverrideFinalConstructTestCaseRector;
@@ -18,9 +20,10 @@
1820
CreateStubOverCreateMockArgRector::class,
1921
ExpressionCreateMockToCreateStubRector::class,
2022
PropertyCreateMockToCreateStubRector::class,
23+
AllowMockObjectsWhereParentClassRector::class,
2124

2225
// experimental, from PHPUnit 12.5.2
2326
// @see https://github.com/sebastianbergmann/phpunit/commit/24c208d6a340c3071f28a9b5cce02b9377adfd43
24-
// AllowMockObjectsWithoutExpectationsAttributeRector::class,
27+
AllowMockObjectsWithoutExpectationsAttributeRector::class,
2528
]);
2629
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\Class_\AllowMockObjectsWhereParentClassRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class AllowMockObjectsWhereParentClassRectorTest extends AbstractRectorTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\Class_\AllowMockObjectsWhereParentClassRector\Fixture;
4+
5+
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
6+
7+
final class SomeClass extends ConstraintValidatorTestCase
8+
{
9+
private \PHPUnit\Framework\MockObject\MockObject $someMock;
10+
}
11+
12+
?>
13+
-----
14+
<?php
15+
16+
namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\Class_\AllowMockObjectsWhereParentClassRector\Fixture;
17+
18+
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
19+
20+
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
21+
final class SomeClass extends ConstraintValidatorTestCase
22+
{
23+
private \PHPUnit\Framework\MockObject\MockObject $someMock;
24+
}
25+
26+
?>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\PHPUnit\PHPUnit120\Rector\Class_\AllowMockObjectsWhereParentClassRector;
7+
8+
return RectorConfig::configure()
9+
->withRules([AllowMockObjectsWhereParentClassRector::class]);
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\PHPUnit120\Rector\Class_;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Attribute;
9+
use PhpParser\Node\AttributeGroup;
10+
use PhpParser\Node\Name\FullyQualified;
11+
use PhpParser\Node\Stmt\Class_;
12+
use PHPStan\Reflection\ClassReflection;
13+
use PHPStan\Reflection\ReflectionProvider;
14+
use Rector\Doctrine\NodeAnalyzer\AttributeFinder;
15+
use Rector\PHPStan\ScopeFetcher;
16+
use Rector\PHPUnit\Enum\PHPUnitAttribute;
17+
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
18+
use Rector\Rector\AbstractRector;
19+
use Rector\Symfony\Enum\SymfonyClass;
20+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
21+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
22+
23+
/**
24+
* @see \Rector\PHPUnit\Tests\PHPUnit120\Rector\Class_\AllowMockObjectsWhereParentClassRector\AllowMockObjectsWhereParentClassRectorTest
25+
*
26+
* @see https://github.com/sebastianbergmann/phpunit/commit/24c208d6a340c3071f28a9b5cce02b9377adfd43
27+
*/
28+
final class AllowMockObjectsWhereParentClassRector extends AbstractRector
29+
{
30+
/**
31+
* @var string[]
32+
*/
33+
private const array PARENT_CLASSES = [SymfonyClass::CONSTRAINT_VALIDATOR_TEST_CASE];
34+
35+
public function __construct(
36+
private readonly TestsNodeAnalyzer $testsNodeAnalyzer,
37+
private readonly AttributeFinder $attributeFinder,
38+
private readonly ReflectionProvider $reflectionProvider,
39+
) {
40+
}
41+
42+
public function getNodeTypes(): array
43+
{
44+
return [Class_::class];
45+
}
46+
47+
/**
48+
* @param Class_ $node
49+
*/
50+
public function refactor(Node $node): ?Class_
51+
{
52+
if ($this->shouldSkipClass($node)) {
53+
return null;
54+
}
55+
56+
if (! $this->hasRelateParentClass($node)) {
57+
return null;
58+
}
59+
60+
// add attribute
61+
$node->attrGroups[] = new AttributeGroup([
62+
new Attribute(new FullyQualified(PHPUnitAttribute::ALLOW_MOCK_OBJECTS_WITHOUT_EXPECTATIONS)),
63+
]);
64+
65+
return $node;
66+
}
67+
68+
public function getRuleDefinition(): RuleDefinition
69+
{
70+
return new RuleDefinition(
71+
'Add #[AllowMockObjectsWithoutExpectations] attribute to PHPUnit test classes with a 3rd party test case, that provides any mocks',
72+
[
73+
new CodeSample(
74+
<<<'CODE_SAMPLE'
75+
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
76+
77+
final class SomeTest extends ConstraintValidatorTestCase
78+
{
79+
}
80+
CODE_SAMPLE
81+
,
82+
<<<'CODE_SAMPLE'
83+
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
84+
85+
#[\PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations]
86+
final class SomeTest extends ConstraintValidatorTestCase
87+
{
88+
}
89+
CODE_SAMPLE
90+
),
91+
]
92+
);
93+
}
94+
95+
private function shouldSkipClass(Class_ $class): bool
96+
{
97+
if (! $this->testsNodeAnalyzer->isInTestClass($class)) {
98+
return true;
99+
}
100+
101+
// attribute must exist for the rule to work
102+
if (! $this->reflectionProvider->hasClass(PHPUnitAttribute::ALLOW_MOCK_OBJECTS_WITHOUT_EXPECTATIONS)) {
103+
return true;
104+
}
105+
106+
// already filled
107+
return $this->attributeFinder->hasAttributeByClasses(
108+
$class,
109+
[PHPUnitAttribute::ALLOW_MOCK_OBJECTS_WITHOUT_EXPECTATIONS]
110+
);
111+
}
112+
113+
private function hasRelateParentClass(Class_ $class): bool
114+
{
115+
$scope = ScopeFetcher::fetch($class);
116+
$classReflection = $scope->getClassReflection();
117+
118+
if (! $classReflection instanceof ClassReflection) {
119+
return false;
120+
}
121+
122+
foreach (self::PARENT_CLASSES as $parentClass) {
123+
if ($classReflection->is($parentClass)) {
124+
return true;
125+
}
126+
}
127+
128+
return false;
129+
}
130+
}

rules/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
namespace Rector\PHPUnit\PHPUnit120\Rector\Class_;
66

7-
use PhpParser\Node\Expr\Variable;
87
use PhpParser\Node;
98
use PhpParser\Node\Attribute;
109
use PhpParser\Node\AttributeGroup;
1110
use PhpParser\Node\Expr\MethodCall;
1211
use PhpParser\Node\Expr\PropertyFetch;
12+
use PhpParser\Node\Expr\Variable;
1313
use PhpParser\Node\Name;
1414
use PhpParser\Node\Name\FullyQualified;
1515
use PhpParser\Node\Stmt\Class_;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Symfony\Component\Validator\Test;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
abstract class ConstraintValidatorTestCase extends TestCase
8+
{
9+
}

0 commit comments

Comments
 (0)