From 5d28ce557fcad24e27b35d92036c30251af56031 Mon Sep 17 00:00:00 2001 From: dominikkaluza Date: Fri, 19 Dec 2025 11:23:34 +0100 Subject: [PATCH 1/2] Move PostConditionsTraitUsedRule to a separate namespace and add tests for it --- phpstan-extension.neon | 2 +- .../PostConditionsTraitUsedRule.php | 2 +- .../PostConditionsTraitUsedRuleTest.php | 92 +++++++++++++++++++ .../__fixtures__/TestWithMockeryTrait.php | 45 +++++++++ .../__fixtures__/TestWithoutMockeryTrait.php | 41 +++++++++ 5 files changed, 180 insertions(+), 2 deletions(-) rename src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/{ => PostConditionsTraitUsedRule}/PostConditionsTraitUsedRule.php (96%) create mode 100644 src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRuleTest.php create mode 100644 src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/TestWithMockeryTrait.php create mode 100644 src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/TestWithoutMockeryTrait.php diff --git a/phpstan-extension.neon b/phpstan-extension.neon index 1285e03..827d11a 100644 --- a/phpstan-extension.neon +++ b/phpstan-extension.neon @@ -1,6 +1,6 @@ services: - - class: BrandEmbassyCodingStandard\PhpStan\Rules\Mockery\PostConditionsTraitUsedRule + class: BrandEmbassyCodingStandard\PhpStan\Rules\Mockery\PostConditionsTraitUsedRule\PostConditionsTraitUsedRule tags: - phpstan.rules.rule - diff --git a/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule.php b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRule.php similarity index 96% rename from src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule.php rename to src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRule.php index 1767204..ef105c5 100644 --- a/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule.php +++ b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRule.php @@ -1,6 +1,6 @@ + */ +class PostConditionsTraitUsedRuleTest extends RuleTestCase +{ + protected function getRule(): Rule + { + return new PostConditionsTraitUsedRule( + new RuleLevelHelper( // @phpstan-ignore phpstanApi.constructor (we will just update it when we update phpstan, it's minor impact) + $this->createReflectionProvider(), + true, + true, + true, + true, + true, + true, + true, + ), + ); + } + + + public function testTestWithoutMockeryTrait(): void + { + $this->analyse( + [__DIR__ . '/__fixtures__/TestWithoutMockeryTrait.php'], + [ + [ + 'Calling expects without Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait.', + 14, + ], + [ + 'Calling once without Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait.', + 16, + ], + [ + 'Calling twice without Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait.', + 19, + ], + [ + 'Calling times without Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait.', + 22, + ], + [ + 'Calling zeroOrMoreTimes without Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait.', + 25, + ], + [ + 'Calling once without Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait.', + 28, + ], + [ + 'Calling atLeast without Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait.', + 28, + ], + [ + 'Calling once without Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait.', + 32, + ], + [ + 'Calling atMost without Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait.', + 32, + ], + [ + 'Calling never without Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait.', + 36, + ], + [ + 'Calling shouldHaveReceived without Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration trait.', + 39, + ], + ], + ); + } + + + public function testTestWithMockeryTrait(): void + { + $this->analyse( + [__DIR__ . '/__fixtures__/TestWithMockeryTrait.php'], + [], + ); + } +} diff --git a/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/TestWithMockeryTrait.php b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/TestWithMockeryTrait.php new file mode 100644 index 0000000..e769f70 --- /dev/null +++ b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/TestWithMockeryTrait.php @@ -0,0 +1,45 @@ +expects('doSomething'); + + $mock->shouldReceive('doSomething') + ->once(); + + $mock->shouldReceive('doSomething') + ->twice(); + + $mock->shouldReceive('doSomething') + ->times(2); + + $mock->shouldReceive('doSomething') + ->zeroOrMoreTimes(); + + $mock->shouldReceive('doSomething') + ->atLeast() + ->once(); + + $mock->shouldReceive('doSomething') + ->atMost() + ->once(); + + $mock->shouldReceive('doSomething') + ->never(); + + $mock->shouldHaveReceived('doSomething'); + } +} diff --git a/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/TestWithoutMockeryTrait.php b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/TestWithoutMockeryTrait.php new file mode 100644 index 0000000..fd35399 --- /dev/null +++ b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/TestWithoutMockeryTrait.php @@ -0,0 +1,41 @@ +expects('doSomething'); + + $mock->shouldReceive('doSomething') + ->once(); + + $mock->shouldReceive('doSomething') + ->twice(); + + $mock->shouldReceive('doSomething') + ->times(2); + + $mock->shouldReceive('doSomething') + ->zeroOrMoreTimes(); + + $mock->shouldReceive('doSomething') + ->atLeast() + ->once(); + + $mock->shouldReceive('doSomething') + ->atMost() + ->once(); + + $mock->shouldReceive('doSomething') + ->never(); + + $mock->shouldHaveReceived('doSomething'); + } +} From 5e338877515db36f9add593d00c534b28fe32f19 Mon Sep 17 00:00:00 2001 From: dominikkaluza Date: Fri, 19 Dec 2025 11:57:58 +0100 Subject: [PATCH 2/2] PostConditionsTraitUsedRule: Skip non-test classes --- .../PostConditionsTraitUsedRule.php | 5 +++ .../PostConditionsTraitUsedRuleTest.php | 9 +++++ .../NonTestClassWithoutMockeryTrait.php | 40 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/NonTestClassWithoutMockeryTrait.php diff --git a/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRule.php b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRule.php index ef105c5..f5e6525 100644 --- a/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRule.php +++ b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRule.php @@ -11,6 +11,7 @@ use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Rules\RuleLevelHelper; use PHPStan\Type\Type; +use PHPUnit\Framework\TestCase; use function in_array; use function sprintf; @@ -91,6 +92,10 @@ public function processNode(Node $node, Scope $scope): array return []; } + if (!$classReflection->isSubclassOf(TestCase::class)) { + return []; + } + $traitName = 'Mockery\\Adapter\\Phpunit\\MockeryPHPUnitIntegration'; if (!$classReflection->hasTraitUse($traitName)) { $errors[] = RuleErrorBuilder::message(sprintf('Calling %s without %s trait.', $name, $traitName)) diff --git a/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRuleTest.php b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRuleTest.php index 9b20b21..d4fec56 100644 --- a/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRuleTest.php +++ b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/PostConditionsTraitUsedRuleTest.php @@ -89,4 +89,13 @@ public function testTestWithMockeryTrait(): void [], ); } + + + public function testNonTestClassWithoutMockeryTrait(): void + { + $this->analyse( + [__DIR__ . '/__fixtures__/NonTestClassWithoutMockeryTrait.php'], + [], + ); + } } diff --git a/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/NonTestClassWithoutMockeryTrait.php b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/NonTestClassWithoutMockeryTrait.php new file mode 100644 index 0000000..1ced19a --- /dev/null +++ b/src/BrandEmbassyCodingStandard/PhpStan/Rules/Mockery/PostConditionsTraitUsedRule/__fixtures__/NonTestClassWithoutMockeryTrait.php @@ -0,0 +1,40 @@ +expects('doSomething'); + + $mock->shouldReceive('doSomething') + ->once(); + + $mock->shouldReceive('doSomething') + ->twice(); + + $mock->shouldReceive('doSomething') + ->times(2); + + $mock->shouldReceive('doSomething') + ->zeroOrMoreTimes(); + + $mock->shouldReceive('doSomething') + ->atLeast() + ->once(); + + $mock->shouldReceive('doSomething') + ->atMost() + ->once(); + + $mock->shouldReceive('doSomething') + ->never(); + + $mock->shouldHaveReceived('doSomething'); + } +}