Skip to content

Commit 7fefc96

Browse files
authored
[phpunit 12] Add CreateStubInCoalesceArgRector (#650)
1 parent a791fc9 commit 7fefc96

File tree

8 files changed

+286
-1
lines changed

8 files changed

+286
-1
lines changed

config/sets/phpunit-code-quality.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
declare(strict_types=1);
44

5-
use Rector\PHPUnit\CodeQuality\Rector\Expression\DecorateWillReturnMapWithExpectsMockRector;
65
use Rector\Config\RectorConfig;
76
use Rector\PHPUnit\CodeQuality\Rector\CallLike\DirectInstanceOverMockArgRector;
87
use Rector\PHPUnit\CodeQuality\Rector\Class_\AddParamTypeFromDependsRector;
@@ -26,6 +25,7 @@
2625
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\RemoveStandaloneCreateMockRector;
2726
use Rector\PHPUnit\CodeQuality\Rector\ClassMethod\ReplaceTestAnnotationWithPrefixedFunctionRector;
2827
use Rector\PHPUnit\CodeQuality\Rector\Expression\AssertArrayCastedObjectToAssertSameRector;
28+
use Rector\PHPUnit\CodeQuality\Rector\Expression\DecorateWillReturnMapWithExpectsMockRector;
2929
use Rector\PHPUnit\CodeQuality\Rector\Foreach_\SimplifyForeachInstanceOfRector;
3030
use Rector\PHPUnit\CodeQuality\Rector\FuncCall\AssertFuncCallToPHPUnitAssertRector;
3131
use Rector\PHPUnit\CodeQuality\Rector\MethodCall\AssertCompareOnCountableWithMethodToAssertCountRector;

config/sets/phpunit120.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
declare(strict_types=1);
44

55
use Rector\Config\RectorConfig;
6+
use Rector\PHPUnit\PHPUnit120\Rector\CallLike\CreateStubInCoalesceArgRector;
67
use Rector\PHPUnit\PHPUnit120\Rector\CallLike\CreateStubOverCreateMockArgRector;
78
use Rector\PHPUnit\PHPUnit120\Rector\Class_\AllowMockObjectsForDataProviderRector;
89
use Rector\PHPUnit\PHPUnit120\Rector\Class_\AllowMockObjectsWhereParentClassRector;
@@ -19,6 +20,7 @@
1920

2021
// stubs over mocks
2122
CreateStubOverCreateMockArgRector::class,
23+
CreateStubInCoalesceArgRector::class,
2224
ExpressionCreateMockToCreateStubRector::class,
2325
PropertyCreateMockToCreateStubRector::class,
2426
AllowMockObjectsWhereParentClassRector::class,
Lines changed: 28 additions & 0 deletions
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\CallLike\CreateStubInCoalesceArgRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class CreateStubInCoalesceArgRectorTest 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: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\CallLike\CreateStubInCoalesceArgRector\Fixture;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
final class HandleArrayItem extends TestCase
8+
{
9+
public function testThat()
10+
{
11+
$value = mt_rand(0, 1);
12+
13+
$items = [
14+
$value ?? $this->createMock(\stdClass::class),
15+
$value ?? $this->createMock(\stdClass::class),
16+
];
17+
}
18+
}
19+
20+
?>
21+
-----
22+
<?php
23+
24+
namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\CallLike\CreateStubInCoalesceArgRector\Fixture;
25+
26+
use PHPUnit\Framework\TestCase;
27+
28+
final class HandleArrayItem extends TestCase
29+
{
30+
public function testThat()
31+
{
32+
$value = mt_rand(0, 1);
33+
34+
$items = [
35+
$value ?? $this->createStub(\stdClass::class),
36+
$value ?? $this->createStub(\stdClass::class),
37+
];
38+
}
39+
}
40+
41+
?>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\CallLike\CreateStubInCoalesceArgRector\Fixture;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
final class SkipInAssign extends TestCase
8+
{
9+
public function testThat()
10+
{
11+
$value = mt_rand(0, 1);
12+
13+
$result = $value ?? $this->createMock(\stdClass::class);
14+
}
15+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\CallLike\CreateStubInCoalesceArgRector\Fixture;
4+
5+
use PHPUnit\Framework\TestCase;
6+
7+
final class SomeFileWithMocks extends TestCase
8+
{
9+
public function testThat()
10+
{
11+
$value = mt_rand(0, 1);
12+
13+
$anyClass = new \stdClass();
14+
$anyClass->run(
15+
$value ?? $this->createMock(\stdClass::class),
16+
$value ?? $this->createMock(\stdClass::class),
17+
);
18+
}
19+
}
20+
21+
?>
22+
-----
23+
<?php
24+
25+
namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\CallLike\CreateStubInCoalesceArgRector\Fixture;
26+
27+
use PHPUnit\Framework\TestCase;
28+
29+
final class SomeFileWithMocks extends TestCase
30+
{
31+
public function testThat()
32+
{
33+
$value = mt_rand(0, 1);
34+
35+
$anyClass = new \stdClass();
36+
$anyClass->run(
37+
$value ?? $this->createStub(\stdClass::class),
38+
$value ?? $this->createStub(\stdClass::class),
39+
);
40+
}
41+
}
42+
43+
?>
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\CallLike\CreateStubInCoalesceArgRector;
7+
8+
return RectorConfig::configure()
9+
->withRules([CreateStubInCoalesceArgRector::class]);
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\PHPUnit\PHPUnit120\Rector\CallLike;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\ArrayItem;
9+
use PhpParser\Node\Expr\BinaryOp\Coalesce;
10+
use PhpParser\Node\Expr\MethodCall;
11+
use PhpParser\Node\Expr\New_;
12+
use PhpParser\Node\Expr\StaticCall;
13+
use PhpParser\Node\Identifier;
14+
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
15+
use Rector\Rector\AbstractRector;
16+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
17+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
18+
19+
/**
20+
* Related change in PHPUnit 12 https://phpunit.expert/articles/testing-with-and-without-dependencies.html
21+
*
22+
* @see \Rector\PHPUnit\Tests\PHPUnit120\Rector\CallLike\CreateStubInCoalesceArgRector\CreateStubInCoalesceArgRectorTest
23+
*/
24+
final class CreateStubInCoalesceArgRector extends AbstractRector
25+
{
26+
public function __construct(
27+
private readonly TestsNodeAnalyzer $testsNodeAnalyzer,
28+
) {
29+
}
30+
31+
public function getRuleDefinition(): RuleDefinition
32+
{
33+
return new RuleDefinition(
34+
'Use createStub() over createMock() when used as argument/array item coalesce ?? fallback',
35+
[
36+
new CodeSample(
37+
<<<'CODE_SAMPLE'
38+
use PHPUnit\Framework\TestCase;
39+
40+
final class SomeTest extends TestCase
41+
{
42+
public function test()
43+
{
44+
$mockObject = $this->>get('service');
45+
$this->someMethod($mockObject ?? $this->createMock(SomeClass::class));
46+
}
47+
48+
private function someMethod($someClass)
49+
{
50+
}
51+
}
52+
CODE_SAMPLE
53+
,
54+
<<<'CODE_SAMPLE'
55+
use PHPUnit\Framework\TestCase;
56+
57+
final class SomeTest extends TestCase
58+
{
59+
public function test()
60+
{
61+
$mockObject = $this->>get('service');
62+
$this->someMethod($mockObject ?? $this->createStub(SomeClass::class));
63+
}
64+
65+
private function someMethod($someClass)
66+
{
67+
}
68+
CODE_SAMPLE
69+
),
70+
71+
]
72+
);
73+
}
74+
75+
/**
76+
* @return array<class-string<Node>>
77+
*/
78+
public function getNodeTypes(): array
79+
{
80+
return [StaticCall::class, MethodCall::class, New_::class, ArrayItem::class];
81+
}
82+
83+
/**
84+
* @param MethodCall|StaticCall|New_|ArrayItem $node
85+
*/
86+
public function refactor(Node $node): MethodCall|StaticCall|New_|ArrayItem|null
87+
{
88+
if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
89+
return null;
90+
}
91+
92+
if ($node instanceof ArrayItem) {
93+
return $this->refactorArrayItem($node);
94+
}
95+
96+
$hasChanges = false;
97+
if ($node->isFirstClassCallable()) {
98+
return null;
99+
}
100+
101+
foreach ($node->getArgs() as $arg) {
102+
if (! $arg->value instanceof Coalesce) {
103+
continue;
104+
}
105+
106+
$coalesce = $arg->value;
107+
if (! $coalesce->right instanceof MethodCall) {
108+
continue;
109+
}
110+
111+
$methodCall = $coalesce->right;
112+
if (! $this->isName($methodCall->name, 'createMock')) {
113+
continue;
114+
}
115+
116+
$methodCall->name = new Identifier('createStub');
117+
$hasChanges = true;
118+
}
119+
120+
if ($hasChanges) {
121+
return $node;
122+
}
123+
124+
return null;
125+
}
126+
127+
private function refactorArrayItem(ArrayItem $arrayItem): ?ArrayItem
128+
{
129+
if (! $arrayItem->value instanceof Coalesce) {
130+
return null;
131+
}
132+
133+
$coalesce = $arrayItem->value;
134+
if (! $coalesce->right instanceof MethodCall) {
135+
return null;
136+
}
137+
138+
$methodCall = $coalesce->right;
139+
if (! $this->isName($methodCall->name, 'createMock')) {
140+
return null;
141+
}
142+
143+
$methodCall->name = new Identifier('createStub');
144+
145+
return $arrayItem;
146+
}
147+
}

0 commit comments

Comments
 (0)