Skip to content

Commit 1e24821

Browse files
committed
[PHPUnit] Add TestListenerToHooksRector
1 parent 8aa22d2 commit 1e24821

File tree

5 files changed

+350
-0
lines changed

5 files changed

+350
-0
lines changed

config/level/phpunit/phpunit90.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
services:
2+
Rector\PHPUnit\Rector\Class_\TestListenerToHooksRector: ~
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Rector\PHPUnit\Rector\Class_;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Identifier;
7+
use PhpParser\Node\Name\FullyQualified;
8+
use PhpParser\Node\Stmt\Class_;
9+
use PhpParser\Node\Stmt\ClassMethod;
10+
use Rector\Rector\AbstractRector;
11+
use Rector\RectorDefinition\CodeSample;
12+
use Rector\RectorDefinition\RectorDefinition;
13+
14+
/**
15+
* @see https://github.com/sebastianbergmann/phpunit/issues/3388
16+
* @see https://github.com/sebastianbergmann/phpunit/commit/34a0abd8b56a4a9de83c9e56384f462541a0f939
17+
*
18+
* @see https://github.com/sebastianbergmann/phpunit/tree/master/src/Runner/Hook
19+
*/
20+
final class TestListenerToHooksRector extends AbstractRector
21+
{
22+
/**
23+
* @var string[][]
24+
*/
25+
private $listenerMethodToHookInterfaces = [
26+
'addIncompleteTest' => ['PHPUnit\Runner\AfterIncompleteTestHook', 'executeAfterIncompleteTest'],
27+
'addRiskyTest' => ['PHPUnit\Runner\AfterRiskyTestHook', 'executeAfterRiskyTest'],
28+
'addSkippedTest' => ['PHPUnit\Runner\AfterSkippedTestHook', 'executeAfterSkippedTest'],
29+
'addError' => ['PHPUnit\Runner\AfterTestErrorHook', 'executeAfterTestError'],
30+
'addFailure' => ['PHPUnit\Runner\AfterTestFailureHook', 'executeAfterTestFailure'],
31+
'addWarning' => ['PHPUnit\Runner\AfterTestWarningHook', 'executeAfterTestWarning'],
32+
# test
33+
'startTest' => ['PHPUnit\Runner\BeforeTestHook', 'executeBeforeTest'],
34+
'endTest' => ['PHPUnit\Runner\AfterTestHook', 'executeAfterTest'],
35+
# suite
36+
'startTestSuite' => ['PHPUnit\Runner\BeforeFirstTestHook', 'executeBeforeFirstTest'],
37+
'endTestSuite' => ['PHPUnit\Runner\AfterLastTestHook', 'executeAfterLastTest'],
38+
];
39+
40+
/**
41+
* @var string
42+
*/
43+
private $testListenerClass;
44+
45+
public function __construct(string $testListenerClass = 'PHPUnit\Framework\TestListener')
46+
{
47+
$this->testListenerClass = $testListenerClass;
48+
}
49+
50+
public function getDefinition(): RectorDefinition
51+
{
52+
return new RectorDefinition('Refactor "*TestListener.php" to particular "*Hook.php" files', [
53+
new CodeSample(
54+
<<<'CODE_SAMPLE'
55+
namespace App\Tests;
56+
57+
use PHPUnit\Framework\TestListener;
58+
59+
final class BeforeListHook implements TestListener
60+
{
61+
public function addError(Test $test, \Throwable $t, float $time): void
62+
{
63+
}
64+
65+
public function addWarning(Test $test, Warning $e, float $time): void
66+
{
67+
}
68+
69+
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
70+
{
71+
}
72+
73+
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
74+
{
75+
}
76+
77+
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
78+
{
79+
}
80+
81+
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
82+
{
83+
}
84+
85+
public function startTestSuite(TestSuite $suite): void
86+
{
87+
}
88+
89+
public function endTestSuite(TestSuite $suite): void
90+
{
91+
}
92+
93+
public function startTest(Test $test): void
94+
{
95+
echo 'start test!';
96+
}
97+
98+
public function endTest(Test $test, float $time): void
99+
{
100+
echo $time;
101+
}
102+
}
103+
CODE_SAMPLE
104+
,
105+
<<<'CODE_SAMPLE'
106+
namespace App\Tests;
107+
108+
final class BeforeListHook implements \PHPUnit\Runner\BeforeTestHook, \PHPUnit\Runner\AfterTestHook
109+
{
110+
public function executeBeforeTest(Test $test): void
111+
{
112+
echo 'start test!';
113+
}
114+
115+
public function executeAfterTest(Test $test, float $time): void
116+
{
117+
echo $time;
118+
}
119+
}
120+
CODE_SAMPLE
121+
),
122+
]);
123+
}
124+
125+
/**
126+
* List of nodes this class checks, classes that implement @see \PhpParser\Node
127+
* @return string[]
128+
*/
129+
public function getNodeTypes(): array
130+
{
131+
return [Class_::class];
132+
}
133+
134+
/**
135+
* Process Node of matched type
136+
* @param Class_ $node
137+
*/
138+
public function refactor(Node $node): ?Node
139+
{
140+
if (! $this->isType($node, $this->testListenerClass)) {
141+
return null;
142+
}
143+
144+
foreach ($node->implements as $implement) {
145+
if ($this->isName($implement, $this->testListenerClass)) {
146+
$this->removeNode($implement);
147+
}
148+
}
149+
150+
foreach ($node->getMethods() as $classMethod) {
151+
$this->processClassMethod($node, $classMethod);
152+
}
153+
154+
return $node;
155+
}
156+
157+
private function processClassMethod(Class_ $class, ClassMethod $classMethod): void
158+
{
159+
foreach ($this->listenerMethodToHookInterfaces as $methodName => $hookClassAndMethod) {
160+
/** @var string $methodName */
161+
if (! $this->isName($classMethod, $methodName)) {
162+
continue;
163+
}
164+
165+
// remove empty methods
166+
if (empty($classMethod->stmts)) {
167+
$this->removeNode($classMethod);
168+
} else {
169+
$class->implements[] = new FullyQualified($hookClassAndMethod[0]);
170+
$classMethod->name = new Identifier($hookClassAndMethod[1]);
171+
}
172+
}
173+
}
174+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\Rector\Class_\TryCatchToExpectExceptionRector\Fixture;
4+
5+
use PHPUnit\Framework\AssertionFailedError;
6+
use PHPUnit\Framework\Test;
7+
use PHPUnit\Framework\TestListener;
8+
use PHPUnit\Framework\TestSuite;
9+
use PHPUnit\Framework\Warning;
10+
11+
final class BeforeListHook implements TestListener
12+
{
13+
public function addError(Test $test, \Throwable $t, float $time): void
14+
{
15+
}
16+
17+
public function addWarning(Test $test, Warning $e, float $time): void
18+
{
19+
}
20+
21+
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
22+
{
23+
}
24+
25+
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
26+
{
27+
}
28+
29+
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
30+
{
31+
}
32+
33+
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
34+
{
35+
}
36+
37+
public function startTestSuite(TestSuite $suite): void
38+
{
39+
}
40+
41+
public function endTestSuite(TestSuite $suite): void
42+
{
43+
}
44+
45+
public function startTest(Test $test): void
46+
{
47+
dump($test);
48+
echo 'start test!';
49+
}
50+
51+
public function endTest(Test $test, float $time): void
52+
{
53+
dump($time);
54+
}
55+
}
56+
57+
?>
58+
-----
59+
<?php
60+
61+
namespace Rector\PHPUnit\Tests\Rector\Class_\TryCatchToExpectExceptionRector\Fixture;
62+
63+
use PHPUnit\Framework\AssertionFailedError;
64+
use PHPUnit\Framework\Test;
65+
use PHPUnit\Framework\TestListener;
66+
use PHPUnit\Framework\TestSuite;
67+
use PHPUnit\Framework\Warning;
68+
69+
final class BeforeListHook implements \PHPUnit\Runner\BeforeTestHook, \PHPUnit\Runner\AfterTestHook
70+
{
71+
public function executeBeforeTest(Test $test): void
72+
{
73+
dump($test);
74+
echo 'start test!';
75+
}
76+
public function executeAfterTest(Test $test, float $time): void
77+
{
78+
dump($time);
79+
}
80+
}
81+
82+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace Rector\PHPUnit\Tests\Rector\Class_\TryCatchToExpectExceptionRector\Fixture;
4+
5+
use PHPUnit\Framework\AssertionFailedError;
6+
use PHPUnit\Framework\Test;
7+
use PHPUnit\Framework\TestListener;
8+
use PHPUnit\Framework\TestSuite;
9+
use PHPUnit\Framework\Warning;
10+
11+
final class SomeListener implements TestListener
12+
{
13+
public function addError(Test $test, \Throwable $t, float $time): void
14+
{
15+
}
16+
17+
public function addWarning(Test $test, Warning $e, float $time): void
18+
{
19+
}
20+
21+
public function addFailure(Test $test, AssertionFailedError $e, float $time): void
22+
{
23+
}
24+
25+
public function addIncompleteTest(Test $test, \Throwable $t, float $time): void
26+
{
27+
}
28+
29+
public function addRiskyTest(Test $test, \Throwable $t, float $time): void
30+
{
31+
}
32+
33+
public function addSkippedTest(Test $test, \Throwable $t, float $time): void
34+
{
35+
}
36+
37+
public function startTestSuite(TestSuite $suite): void
38+
{
39+
}
40+
41+
public function endTestSuite(TestSuite $suite): void
42+
{
43+
}
44+
45+
public function startTest(Test $test): void
46+
{
47+
}
48+
49+
public function endTest(Test $test, float $time): void
50+
{
51+
}
52+
}
53+
54+
?>
55+
-----
56+
<?php
57+
58+
namespace Rector\PHPUnit\Tests\Rector\Class_\TryCatchToExpectExceptionRector\Fixture;
59+
60+
use PHPUnit\Framework\AssertionFailedError;
61+
use PHPUnit\Framework\Test;
62+
use PHPUnit\Framework\TestListener;
63+
use PHPUnit\Framework\TestSuite;
64+
use PHPUnit\Framework\Warning;
65+
66+
final class SomeListener implements TestListener
67+
{
68+
}
69+
70+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Rector\PHPUnit\Tests\Rector\Class_\TestListenerToHooksRector;
4+
5+
use Rector\PHPUnit\Rector\Class_\TestListenerToHooksRector;
6+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
7+
8+
final class TestListenerToHooksRectorTest extends AbstractRectorTestCase
9+
{
10+
public function test(): void
11+
{
12+
$this->doTestFiles([
13+
__DIR__ . '/Fixture/clear_it_all.php.inc',
14+
__DIR__ . '/Fixture/before_list_hook.php.inc',
15+
]);
16+
}
17+
18+
public function getRectorClass(): string
19+
{
20+
return TestListenerToHooksRector::class;
21+
}
22+
}

0 commit comments

Comments
 (0)