-
Notifications
You must be signed in to change notification settings - Fork 0
Add PHPStan rule enforcing strict mocking #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 {} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.