diff --git a/rules-tests/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector/DecorateWillReturnMapWithExpectsMockRectorTest.php b/rules-tests/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector/DecorateWillReturnMapWithExpectsMockRectorTest.php new file mode 100644 index 00000000..feb4a436 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector/DecorateWillReturnMapWithExpectsMockRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector/Fixture/fixture.php.inc b/rules-tests/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector/Fixture/fixture.php.inc new file mode 100644 index 00000000..9a3f8eae --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector/Fixture/fixture.php.inc @@ -0,0 +1,33 @@ +createMock(\stdClass::class); + $someMock->method('some')->willReturnMap([1, 2]); + } +} + +?> +----- +createMock(\stdClass::class); + $someMock->expects($this->exactly(2))->method('some')->willReturnMap([1, 2]); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector/config/configured_rule.php new file mode 100644 index 00000000..0b1c2811 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([DecorateWillReturnMapWithExpectsMockRector::class]); diff --git a/rules/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector.php b/rules/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector.php new file mode 100644 index 00000000..2b9ea26b --- /dev/null +++ b/rules/CodeQuality/Rector/Expression/DecorateWillReturnMapWithExpectsMockRector.php @@ -0,0 +1,133 @@ +someMock = $this->createMock(SomeClass::class); + + $this->someMock->method("someMethod") + ->willReturnMap([ + ["arg1", "arg2", "result1"], + ["arg3", "arg4", "result2"], + ]); + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; + +final class SomeTest extends TestCase +{ + private MockObject $someMock; + + protected function setUp(): void + { + $this->someMock = $this->createMock(SomeClass::class); + + $this->someMock->expects($this->exactly(2)) + ->method("someMethod") + ->willReturnMap([ + ["arg1", "arg2", "result1"], + ["arg3", "arg4", "result2"], + ]); + } +} +CODE_SAMPLE + ), + + ]); + } + + public function getNodeTypes(): array + { + return [Expression::class]; + } + + /** + * @param Expression $node + * @return Expression|null + */ + public function refactor(Node $node) + { + if (! $node->expr instanceof MethodCall) { + return null; + } + + $methodCall = $node->expr; + if (! $this->isName($methodCall->name, 'willReturnMap')) { + return null; + } + + $topmostCall = $this->resolveTopmostCall($methodCall); + + // already covered + if ($this->isName($topmostCall->name, 'expects')) { + return null; + } + + // count values in will map arg + $willReturnMapArg = $methodCall->getArgs()[0]; + if (! $willReturnMapArg->value instanceof Array_) { + return null; + } + + $array = $willReturnMapArg->value; + $mapCount = count($array->items); + + $topmostCall->var = new MethodCall( + $topmostCall->var, + new Identifier('expects'), + [new Arg(new MethodCall( + new Variable('this'), + new Identifier('exactly'), + [new Arg(new Int_($mapCount))] + ))] + ); + + return $node; + } + + private function resolveTopmostCall(MethodCall $methodCall): MethodCall + { + $currentCall = $methodCall; + while ($currentCall->var instanceof MethodCall) { + $currentCall = $currentCall->var; + } + + return $currentCall; + } +}