Skip to content

Commit

Permalink
Updated Mock trait to be compatible with PHPUnit 11.
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexSkrypnyk committed Dec 16, 2024
1 parent f771d55 commit f3204f4
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 46 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
75 changes: 31 additions & 44 deletions tests/phpunit/Traits/MockTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace YourNamespace\App\Tests\Traits;

use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\Stub\Stub;

/**
* Trait MockTrait.
Expand All @@ -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<non-empty-string,mixed> $methods_map
* Optional array of methods and values, keyed by method name.
* @param array<string,mixed>|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<string, scalar|\Closure> $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<mixed> $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);
}
}

Expand Down
1 change: 1 addition & 0 deletions tests/phpunit/Unit/Command/CommandTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion tests/phpunit/Unit/Command/JokeCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit f3204f4

Please sign in to comment.