Skip to content

Commit 12af219

Browse files
authored
PHPUnit: support #[DataProviderExternal] (#212)
1 parent e60e67b commit 12af219

File tree

2 files changed

+74
-10
lines changed

2 files changed

+74
-10
lines changed

src/Provider/PhpUnitUsageProvider.php

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
use PHPStan\PhpDocParser\Lexer\Lexer;
1111
use PHPStan\PhpDocParser\Parser\PhpDocParser;
1212
use PHPStan\PhpDocParser\Parser\TokenIterator;
13-
use PHPStan\Reflection\ExtendedMethodReflection;
1413
use PHPUnit\Framework\TestCase;
1514
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodRef;
1615
use ShipMonk\PHPStan\DeadCode\Graph\ClassMethodUsage;
@@ -48,21 +47,27 @@ public function getUsages(Node $node, Scope $scope): array
4847
}
4948

5049
$usages = [];
50+
$className = $classReflection->getName();
5151

5252
foreach ($classReflection->getNativeReflection()->getMethods() as $method) {
53-
$dataProviders = array_merge(
53+
$methodName = $method->getName();
54+
55+
$externalDataProviderMethods = $this->getExternalDataProvidersFromAttributes($method);
56+
$localDataProviderMethods = array_merge(
5457
$this->getDataProvidersFromAnnotations($method->getDocComment()),
5558
$this->getDataProvidersFromAttributes($method),
5659
);
5760

58-
foreach ($dataProviders as $dataProvider) {
59-
if ($classReflection->hasNativeMethod($dataProvider)) {
60-
$usages[] = $this->createUsage($classReflection->getNativeMethod($dataProvider), 'Data provider method');
61-
}
61+
foreach ($externalDataProviderMethods as [$externalClassName, $externalMethodName]) {
62+
$usages[] = $this->createUsage($externalClassName, $externalMethodName, "External data provider method, used by $className::$methodName");
63+
}
64+
65+
foreach ($localDataProviderMethods as $dataProvider) {
66+
$usages[] = $this->createUsage($className, $dataProvider, "Data provider method, used by $methodName");
6267
}
6368

6469
if ($this->isTestCaseMethod($method)) {
65-
$usages[] = $this->createUsage($classReflection->getNativeMethod($method->getName()), 'Test method');
70+
$usages[] = $this->createUsage($className, $methodName, 'Test method');
6671
}
6772
}
6873

@@ -128,6 +133,25 @@ private function getDataProvidersFromAttributes(ReflectionMethod $method): array
128133
return $result;
129134
}
130135

136+
/**
137+
* @return list<array{string, string}>
138+
*/
139+
private function getExternalDataProvidersFromAttributes(ReflectionMethod $method): array
140+
{
141+
$result = [];
142+
143+
foreach ($method->getAttributes('PHPUnit\Framework\Attributes\DataProviderExternal') as $providerAttributeReflection) {
144+
$className = $providerAttributeReflection->getArguments()[0] ?? $providerAttributeReflection->getArguments()['className'] ?? null;
145+
$methodName = $providerAttributeReflection->getArguments()[1] ?? $providerAttributeReflection->getArguments()['methodName'] ?? null;
146+
147+
if (is_string($className) && is_string($methodName)) {
148+
$result[] = [$className, $methodName];
149+
}
150+
}
151+
152+
return $result;
153+
}
154+
131155
private function hasAttribute(ReflectionMethod $method, string $attributeClass): bool
132156
{
133157
return $method->getAttributes($attributeClass) !== [];
@@ -142,13 +166,13 @@ private function hasAnnotation(ReflectionMethod $method, string $string): bool
142166
return strpos($method->getDocComment(), $string) !== false;
143167
}
144168

145-
private function createUsage(ExtendedMethodReflection $getNativeMethod, string $reason): ClassMethodUsage
169+
private function createUsage(string $className, string $methodName, string $reason): ClassMethodUsage
146170
{
147171
return new ClassMethodUsage(
148172
UsageOrigin::createVirtual($this, VirtualUsageData::withNote($reason)),
149173
new ClassMethodRef(
150-
$getNativeMethod->getDeclaringClass()->getName(),
151-
$getNativeMethod->getName(),
174+
$className,
175+
$methodName,
152176
false,
153177
),
154178
);

tests/Rule/data/providers/phpunit.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use PHPUnit\Framework\Attributes\Before;
66
use PHPUnit\Framework\Attributes\DataProvider;
7+
use PHPUnit\Framework\Attributes\DataProviderExternal;
78
use PHPUnit\Framework\Attributes\Test;
89
use PHPUnit\Framework\TestCase;
910

@@ -14,6 +15,18 @@ public function callBefore(): void
1415
}
1516
}
1617

18+
class ExternalProvider {
19+
public static function provideOne(): array
20+
{
21+
return [];
22+
}
23+
24+
public static function provideTwo(): array
25+
{
26+
return [];
27+
}
28+
}
29+
1730
class SomeTest extends TestCase
1831
{
1932
use TraitTestCase {
@@ -30,6 +43,16 @@ public function testFoo2(string $arg): void
3043
{
3144
}
3245

46+
#[DataProviderExternal(ExternalProvider::class, 'provideOne')]
47+
public function testExternal1(string $arg): void
48+
{
49+
}
50+
51+
#[DataProviderExternal(methodName: 'provideTwo', className: ExternalProvider::class)]
52+
public function testExternal2(string $arg): void
53+
{
54+
}
55+
3356
/**
3457
* @dataProvider provideFromPhpDoc
3558
*/
@@ -98,3 +121,20 @@ public static function providerTest(): array
98121
return [];
99122
}
100123
}
124+
125+
126+
abstract class TestCaseParent extends TestCase
127+
{
128+
public static function providerInParent(): array
129+
{
130+
return [];
131+
}
132+
}
133+
134+
final class TestProviderInParent extends TestCaseParent
135+
{
136+
#[DataProvider('providerInParent')]
137+
public function testFoo(): void
138+
{
139+
}
140+
}

0 commit comments

Comments
 (0)