From afe1113809ab23ca2e3f4dc9a8aeb4c4aba91211 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 3 Feb 2026 23:56:46 +0100 Subject: [PATCH] add fixture --- .../add_to_setup_when_no_expects.php.inc | 54 +++++++++++++++++++ ...ectsWithoutExpectationsAttributeRector.php | 38 +++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 rules-tests/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector/Fixture/add_to_setup_when_no_expects.php.inc diff --git a/rules-tests/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector/Fixture/add_to_setup_when_no_expects.php.inc b/rules-tests/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector/Fixture/add_to_setup_when_no_expects.php.inc new file mode 100644 index 00000000..a4dddd3b --- /dev/null +++ b/rules-tests/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector/Fixture/add_to_setup_when_no_expects.php.inc @@ -0,0 +1,54 @@ +someMock = $this->createMock(\stdClass::class); + $this->someMock->method('some')->willReturn(true); + } + + public function testOne() + { + } + + public function testTwo() + { + } +} + +?> +----- +someMock = $this->createMock(\stdClass::class); + $this->someMock->method('some')->willReturn(true); + } + + public function testOne() + { + } + + public function testTwo() + { + } +} + +?> diff --git a/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector.php b/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector.php index 14c23faf..748f6cf9 100644 --- a/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector.php +++ b/rules/PHPUnit120/Rector/Class_/AllowMockObjectsWithoutExpectationsAttributeRector.php @@ -14,6 +14,7 @@ use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Type\ObjectType; use Rector\Doctrine\NodeAnalyzer\AttributeFinder; use Rector\PhpParser\Node\BetterNodeFinder; use Rector\PHPUnit\Enum\PHPUnitAttribute; @@ -86,6 +87,16 @@ public function refactor(Node $node): ?Class_ } } + // or find a ->method() calls on a setUp() mocked property + $hasAnyMethodInSetup = $this->isMissingExpectsOnMockObjectMethodCallInSetUp($node); + if ($hasAnyMethodInSetup && $testMethodCount > 1) { + $node->attrGroups[] = new AttributeGroup([ + new Attribute(new FullyQualified(PHPUnitAttribute::ALLOW_MOCK_OBJECTS_WITHOUT_EXPECTATIONS)), + ]); + + return $node; + } + if (! $this->shouldAddAttribute($missedTestMethodsByMockPropertyName)) { return null; } @@ -269,4 +280,31 @@ private function isAtLeastOneMockPropertyMockedOnce(array $usingTestMethodsByMoc return false; } + + private function isMissingExpectsOnMockObjectMethodCallInSetUp(Class_ $class): bool + { + $setupClassMethod = $class->getMethod(MethodName::SET_UP); + if (! $setupClassMethod instanceof ClassMethod) { + return false; + } + + /** @var MethodCall[] $methodCalls */ + $methodCalls = $this->betterNodeFinder->findInstancesOfScoped( + (array) $setupClassMethod->stmts, + MethodCall::class + ); + foreach ($methodCalls as $methodCall) { + if (! $this->isName($methodCall->name, 'method')) { + continue; + } + + if (! $this->isObjectType($methodCall->var, new ObjectType(PHPUnitClassName::MOCK_OBJECT))) { + continue; + } + + return true; + } + + return false; + } }