-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add PHPStan rule enforcing strict mocking (#11)
Co-authored-by: Ben Challis <ben-challis@users.noreply.github.com>
- Loading branch information
1 parent
74051b8
commit 3d04736
Showing
13 changed files
with
265 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
conditionalTags: | ||
Lendable\PHPUnitExtensions\Phpstan\Rule\EnforceStrictMocking: | ||
phpstan.rules.rule: %lendable_phpunit.enforceStrictMocking.enabled% | ||
|
||
parametersSchema: | ||
lendable_phpunit: structure([ | ||
enforceStrictMocking: structure([ | ||
enabled: bool() | ||
pardoned: listOf(string()) | ||
]) | ||
]) | ||
|
||
parameters: | ||
lendable_phpunit: | ||
enforceStrictMocking: | ||
enabled: true | ||
pardoned: [] | ||
|
||
services: | ||
- | ||
class: Lendable\PHPUnitExtensions\Phpstan\Rule\EnforceStrictMocking | ||
arguments: | ||
pardoned: %lendable_phpunit.enforceStrictMocking.pardoned% |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Lendable\PHPUnitExtensions\Phpstan\Rule; | ||
|
||
use Lendable\PHPUnitExtensions\StrictMocking as StrictMockingTrait; | ||
use Lendable\PHPUnitExtensions\TestCase as StrictMockingTestCase; | ||
use PhpParser\Node; | ||
use PhpParser\Node\Name; | ||
use PhpParser\Node\Stmt\Class_; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Reflection\ClassReflection; | ||
use PHPStan\Rules\Rule; | ||
use PHPStan\Rules\RuleErrorBuilder; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
/** | ||
* @implements Rule<Class_> | ||
*/ | ||
final class EnforceStrictMocking implements Rule | ||
{ | ||
/** | ||
* @var array<class-string, int> | ||
*/ | ||
private readonly array $pardoned; | ||
|
||
/** | ||
* @param list<class-string> $pardoned | ||
*/ | ||
public function __construct(array $pardoned) | ||
{ | ||
$this->pardoned = \array_flip($pardoned); | ||
} | ||
|
||
public function getNodeType(): string | ||
{ | ||
return Class_::class; | ||
} | ||
|
||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
if (!$node->namespacedName instanceof Name) { | ||
return []; | ||
} | ||
|
||
if (!$node->extends instanceof Name) { | ||
return []; | ||
} | ||
|
||
if ($node->isAbstract()) { | ||
return []; | ||
} | ||
|
||
$className = $node->namespacedName->toString(); | ||
if (!\str_ends_with($className, 'Test')) { | ||
return []; | ||
} | ||
|
||
if (isset($this->pardoned[$className])) { | ||
return []; | ||
} | ||
|
||
$reflection = $scope->resolveTypeByName($node->namespacedName)->getClassReflection(); | ||
if (!$reflection instanceof ClassReflection) { | ||
return []; | ||
} | ||
|
||
$parents = $reflection->getParentClassesNames(); | ||
if (!\in_array(TestCase::class, $parents, true)) { | ||
return []; | ||
} | ||
|
||
if (\in_array(StrictMockingTestCase::class, $parents, true)) { | ||
return []; | ||
} | ||
|
||
if (isset($reflection->getTraits(true)[StrictMockingTrait::class])) { | ||
return []; | ||
} | ||
|
||
$ruleErrorBuilder = RuleErrorBuilder::message(\sprintf( | ||
'Class "%s" must either extend "%s" or use "%s" trait.', | ||
$className, | ||
StrictMockingTestCase::class, | ||
StrictMockingTrait::class, | ||
)); | ||
|
||
return [$ruleErrorBuilder->build()]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\Phpstan\Lendable\PHPUnitExtensions\Rule; | ||
|
||
use Lendable\PHPUnitExtensions\Phpstan\Rule\EnforceStrictMocking; | ||
use Lendable\PHPUnitExtensions\StrictMocking; | ||
use Lendable\PHPUnitExtensions\TestCase; | ||
use PHPStan\Testing\RuleTestCase; | ||
use PHPUnit\Framework\Attributes\CoversClass; | ||
use PHPUnit\Framework\Attributes\Test; | ||
use Tests\Phpstan\Lendable\PHPUnitExtensions\data\IndirectlyExtendingTest; | ||
use Tests\Phpstan\Lendable\PHPUnitExtensions\data\TestCaseTest; | ||
|
||
#[CoversClass(EnforceStrictMocking::class)] | ||
final class EnforceExtendedClassTest extends RuleTestCase | ||
{ | ||
#[Test] | ||
public function reports_test_directly_extending_phpunits_test_case(): void | ||
{ | ||
$this->analyse([__DIR__.'/../data/TestCaseTest.php'], [ | ||
[ | ||
$this->errorMessageFor(TestCaseTest::class), | ||
9, | ||
], | ||
]); | ||
} | ||
|
||
#[Test] | ||
public function does_not_report_abstract_test_directly_extending_phpunits_test_case(): void | ||
{ | ||
$this->analyse([__DIR__.'/../data/AbstractTestCaseTest.php'], []); | ||
} | ||
|
||
#[Test] | ||
public function reports_test_indirectly_extending_phpunits_test_case(): void | ||
{ | ||
$this->analyse([__DIR__.'/../data/IndirectlyExtendingTest.php'], [ | ||
[ | ||
$this->errorMessageFor(IndirectlyExtendingTest::class), | ||
7, | ||
], | ||
]); | ||
} | ||
|
||
#[Test] | ||
public function does_not_report_test_extending_strict_mocking(): void | ||
{ | ||
$this->analyse([__DIR__.'/../data/StrictMockingTestCaseTest.php'], []); | ||
} | ||
|
||
#[Test] | ||
public function does_not_report_test_directly_using_strict_mocking_trait(): void | ||
{ | ||
$this->analyse([__DIR__.'/../data/StrictMockingTraitTest.php'], []); | ||
} | ||
|
||
#[Test] | ||
public function does_not_report_test_indirectly_using_strict_mocking_trait(): void | ||
{ | ||
$this->analyse([__DIR__.'/../data/IndirectStrictMockingTraitTest.php'], []); | ||
} | ||
|
||
protected function getRule(): EnforceStrictMocking | ||
{ | ||
return new EnforceStrictMocking([]); | ||
} | ||
|
||
private function errorMessageFor(string $class): string | ||
{ | ||
return \sprintf( | ||
'Class "%s" must either extend "%s" or use "%s" trait.', | ||
$class, | ||
TestCase::class, | ||
StrictMocking::class, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\Phpstan\Lendable\PHPUnitExtensions\data; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
|
||
abstract class AbstractTestCaseTest extends TestCase {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\Phpstan\Lendable\PHPUnitExtensions\data; | ||
|
||
final class IndirectStrictMockingTraitTest extends StrictMockingTraitTest {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\Phpstan\Lendable\PHPUnitExtensions\data; | ||
|
||
class IndirectlyExtendingTest extends TestCaseTest {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\Phpstan\Lendable\PHPUnitExtensions\data; | ||
|
||
use Lendable\PHPUnitExtensions\TestCase; | ||
|
||
final class StrictMockingTestCaseTest extends TestCase {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\Phpstan\Lendable\PHPUnitExtensions\data; | ||
|
||
use Lendable\PHPUnitExtensions\StrictMocking; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class StrictMockingTraitTest extends TestCase | ||
{ | ||
use StrictMocking; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\Phpstan\Lendable\PHPUnitExtensions\data; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
|
||
class TestCaseTest extends TestCase {} |