From f3204f4b131623a5cc2c5b61e4946a74a57c3ddc Mon Sep 17 00:00:00 2001 From: Alex Skrypnyk Date: Tue, 17 Dec 2024 08:58:26 +1100 Subject: [PATCH] Updated Mock trait to be compatible with PHPUnit 11. --- composer.json | 2 +- tests/phpunit/Traits/MockTrait.php | 75 ++++++++----------- .../phpunit/Unit/Command/CommandTestCase.php | 1 + .../phpunit/Unit/Command/JokeCommandTest.php | 2 +- 4 files changed, 34 insertions(+), 46 deletions(-) diff --git a/composer.json b/composer.json index 130b9d8..08d7ebc 100644 --- a/composer.json +++ b/composer.json @@ -68,8 +68,8 @@ ], "lint": [ "cp php-script php-script.php && phpcs; rm php-script.php", - "phpmd --exclude vendor,vendor-bin,node_modules . text phpmd.xml", "phpstan", + "phpmd --exclude vendor,vendor-bin,node_modules . text phpmd.xml", "rector --clear-cache --dry-run" ], "lint-fix": [ diff --git a/tests/phpunit/Traits/MockTrait.php b/tests/phpunit/Traits/MockTrait.php index a22a6d9..3c4c12c 100644 --- a/tests/phpunit/Traits/MockTrait.php +++ b/tests/phpunit/Traits/MockTrait.php @@ -5,7 +5,6 @@ namespace YourNamespace\App\Tests\Traits; use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\MockObject\Stub\Stub; /** * Trait MockTrait. @@ -15,66 +14,54 @@ trait MockTrait { /** - * Prepare class mock. + * Helper to prepare class or trait mock. * - * @param string $class - * Class name to generate the mock. - * @param array $methods_map - * Optional array of methods and values, keyed by method name. - * @param array|bool $args - * Optional array of constructor arguments. If omitted, a constructor will - * not be called. If TRUE, the original constructor will be called as-is. + * @param class-string $class + * Class or trait name to generate the mock. + * @param array $methods + * Optional array of methods and values, keyed by method name. Array + * elements can be return values, callbacks created with + * $this->willReturnCallback(), or closures. + * @param bool|array $args + * Optional array of constructor arguments or FALSE to disable the original + * constructor. If omitted, an original constructor will be called. * * @return \PHPUnit\Framework\MockObject\MockObject - * An instance of the mock. + * Mocked class. * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @throws \ReflectionException + * + * @SuppressWarnings(CyclomaticComplexity) */ - protected function prepareMock(string $class, array $methods_map = [], array|bool $args = []): MockObject { - $methods = array_filter(array_keys($methods_map), 'is_string'); + protected function prepareMock(string $class, array $methods = [], array|bool $args = []): MockObject { + $methods = array_filter($methods, fn($value, $key): bool => is_string($key), ARRAY_FILTER_USE_BOTH); if (!class_exists($class)) { throw new \InvalidArgumentException(sprintf('Class %s does not exist', $class)); } - $reflection_class = new \ReflectionClass($class); + $builder = $this->getMockBuilder($class); - if ($reflection_class->isAbstract()) { - $mock = $this->getMockForAbstractClass($class, is_array($args) ? $args : [], '', !empty($args), TRUE, TRUE, $methods); + if (is_array($args) && !empty($args)) { + $builder->enableOriginalConstructor()->setConstructorArgs($args); } - else { - $mock = $this->getMockBuilder($class); - if (is_array($args) && !empty($args)) { - $mock = $mock->enableOriginalConstructor() - ->setConstructorArgs($args); - } - elseif ($args === FALSE) { - $mock = $mock->disableOriginalConstructor(); - } - - if (!empty($methods)) { - $mock = $mock->onlyMethods($methods); - } - - $mock = $mock->getMock(); + elseif ($args === FALSE) { + $builder->disableOriginalConstructor(); } - foreach ($methods_map as $method => $value) { - // Handle callback values differently. - if ($value instanceof Stub) { - $mock->expects($this->any()) - ->method($method) - ->will($value); + $method_names = array_filter(array_keys($methods), fn($method): bool => is_string($method) && !empty($method)); + $mock = $builder->onlyMethods($method_names)->getMock(); + + foreach ($methods as $method => $value) { + // Handle callback value differently based on its type. + if (is_object($value) && str_contains($value::class, 'Callback')) { + $mock->expects($this->any())->method($method)->willReturnCallback($value); } - elseif (is_callable($value)) { - $mock->expects($this->any()) - ->method($method) - ->willReturnCallback($value); + elseif (is_object($value) && str_contains($value::class, 'Closure')) { + $mock->expects($this->any())->method($method)->willReturnCallback($value); } else { - $mock->expects($this->any()) - ->method($method) - ->willReturn($value); + $mock->expects($this->any())->method($method)->willReturn($value); } } diff --git a/tests/phpunit/Unit/Command/CommandTestCase.php b/tests/phpunit/Unit/Command/CommandTestCase.php index 4c77f45..18dcd0b 100644 --- a/tests/phpunit/Unit/Command/CommandTestCase.php +++ b/tests/phpunit/Unit/Command/CommandTestCase.php @@ -53,6 +53,7 @@ protected function runExecute(string|object $object_or_class, array $input = [], /** @var string $name */ $name = $this->getProtectedValue($instance, 'defaultName'); } + $command = $application->find($name); $this->commandTester = new CommandTester($command); diff --git a/tests/phpunit/Unit/Command/JokeCommandTest.php b/tests/phpunit/Unit/Command/JokeCommandTest.php index 7ebe406..15101bc 100644 --- a/tests/phpunit/Unit/Command/JokeCommandTest.php +++ b/tests/phpunit/Unit/Command/JokeCommandTest.php @@ -26,7 +26,7 @@ public function testExecute(string $content, int $expected_code, array|string $e /** @var \YourNamespace\App\Command\JokeCommand $mock */ $mock = $this->prepareMock(JokeCommand::class, [ 'getContent' => $content, - ], TRUE); + ]); $mock->setName('joke'); $output = $this->runExecute($mock);