diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 29053d3..e5d071b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,7 @@ jobs: strategy: matrix: php-version: + - '8.4' - '8.3' - '8.2' - '8.1' @@ -23,6 +24,10 @@ jobs: - '7.1' - '7.0' phpunit-version: + - '12.0.0' + - '11.5.0' + - '11.4.0' + - '11.3.0' - '11.2.0' - '11.1.0' - '11.0.0' @@ -58,6 +63,52 @@ jobs: - '6.0.0' exclude: + # PHP 8.4 Exclusions + - php-version: '8.4' + phpunit-version: '9.4.0' + - php-version: '8.4' + phpunit-version: '9.3.0' + - php-version: '8.4' + phpunit-version: '9.2.0' + - php-version: '8.4' + phpunit-version: '9.1.0' + - php-version: '8.4' + phpunit-version: '9.0.0' + - php-version: '8.4' + phpunit-version: '8.4.0' + - php-version: '8.4' + phpunit-version: '8.3.0' + - php-version: '8.4' + phpunit-version: '8.2.0' + - php-version: '8.4' + phpunit-version: '8.1.0' + - php-version: '8.4' + phpunit-version: '8.0.0' + - php-version: '8.4' + phpunit-version: '7.5.0' + - php-version: '8.4' + phpunit-version: '7.4.0' + - php-version: '8.4' + phpunit-version: '7.3.0' + - php-version: '8.4' + phpunit-version: '7.2.0' + - php-version: '8.4' + phpunit-version: '7.1.0' + - php-version: '8.4' + phpunit-version: '7.0.0' + - php-version: '8.4' + phpunit-version: '6.5.0' + - php-version: '8.4' + phpunit-version: '6.4.0' + - php-version: '8.4' + phpunit-version: '6.3.0' + - php-version: '8.4' + phpunit-version: '6.2.0' + - php-version: '8.4' + phpunit-version: '6.1.0' + - php-version: '8.4' + phpunit-version: '6.0.0' + # PHP 8.3 Exclusions - php-version: '8.3' phpunit-version: '9.4.0' @@ -105,6 +156,8 @@ jobs: phpunit-version: '6.0.0' # PHP 8.2 Exclusions + - php-version: '8.2' + phpunit-version: '12.0.0' - php-version: '8.2' phpunit-version: '9.4.0' - php-version: '8.2' @@ -151,6 +204,14 @@ jobs: phpunit-version: '6.0.0' # PHP 8.1 Exclusions + - php-version: '8.1' + phpunit-version: '12.0.0' + - php-version: '8.1' + phpunit-version: '11.5.0' + - php-version: '8.1' + phpunit-version: '11.4.0' + - php-version: '8.1' + phpunit-version: '11.3.0' - php-version: '8.1' phpunit-version: '11.2.0' - php-version: '8.1' @@ -203,6 +264,14 @@ jobs: phpunit-version: '6.0.0' # PHP 8.0 Exclusions + - php-version: '8.0' + phpunit-version: '12.0.0' + - php-version: '8.0' + phpunit-version: '11.5.0' + - php-version: '8.0' + phpunit-version: '11.4.0' + - php-version: '8.0' + phpunit-version: '11.3.0' - php-version: '8.0' phpunit-version: '11.2.0' - php-version: '8.0' @@ -263,6 +332,14 @@ jobs: phpunit-version: '6.0.0' # PHP 7.4 Exclusions + - php-version: '7.4' + phpunit-version: '12.0.0' + - php-version: '7.4' + phpunit-version: '11.5.0' + - php-version: '7.4' + phpunit-version: '11.4.0' + - php-version: '7.4' + phpunit-version: '11.3.0' - php-version: '7.4' phpunit-version: '11.2.0' - php-version: '7.4' @@ -309,6 +386,14 @@ jobs: phpunit-version: '6.0.0' # PHP 7.3 Exclusions + - php-version: '7.3' + phpunit-version: '12.0.0' + - php-version: '7.3' + phpunit-version: '11.5.0' + - php-version: '7.3' + phpunit-version: '11.4.0' + - php-version: '7.3' + phpunit-version: '11.3.0' - php-version: '7.3' phpunit-version: '11.2.0' - php-version: '7.3' @@ -329,6 +414,14 @@ jobs: phpunit-version: '10.0.0' # PHP 7.2 Exclusions + - php-version: '7.2' + phpunit-version: '12.0.0' + - php-version: '7.2' + phpunit-version: '11.5.0' + - php-version: '7.2' + phpunit-version: '11.4.0' + - php-version: '7.2' + phpunit-version: '11.3.0' - php-version: '7.2' phpunit-version: '11.2.0' - php-version: '7.2' @@ -363,6 +456,14 @@ jobs: phpunit-version: '9.0.0' # PHP 7.1 Exclusions + - php-version: '7.1' + phpunit-version: '12.0.0' + - php-version: '7.1' + phpunit-version: '11.5.0' + - php-version: '7.1' + phpunit-version: '11.4.0' + - php-version: '7.1' + phpunit-version: '11.3.0' - php-version: '7.1' phpunit-version: '11.2.0' - php-version: '7.1' @@ -409,6 +510,14 @@ jobs: phpunit-version: '8.0.0' # PHP 7.0 Exclusions + - php-version: '7.0' + phpunit-version: '12.0.0' + - php-version: '7.0' + phpunit-version: '11.5.0' + - php-version: '7.0' + phpunit-version: '11.4.0' + - php-version: '7.0' + phpunit-version: '11.3.0' - php-version: '7.0' phpunit-version: '11.2.0' - php-version: '7.0' diff --git a/autoload.php b/autoload.php index e59f8ca..f567a6a 100644 --- a/autoload.php +++ b/autoload.php @@ -90,6 +90,11 @@ class_alias( } if ($hasVersion + && version_compare(\PHPUnit\Runner\Version::id(), '12.0.0') >= 0 +) { + class_alias(\phpmock\phpunit\DefaultArgumentRemoverReturnTypes120::class, \phpmock\phpunit\DefaultArgumentRemover::class); + class_alias(\phpmock\phpunit\MockObjectProxyReturnTypes120::class, \phpmock\phpunit\MockObjectProxy::class); +} elseif ($hasVersion && version_compare(\PHPUnit\Runner\Version::id(), '10.0.0') >= 0 ) { class_alias(\phpmock\phpunit\DefaultArgumentRemoverReturnTypes100::class, \phpmock\phpunit\DefaultArgumentRemover::class); diff --git a/classes/DefaultArgumentRemoverReturnTypes120.php b/classes/DefaultArgumentRemoverReturnTypes120.php new file mode 100644 index 0000000..8cce8fa --- /dev/null +++ b/classes/DefaultArgumentRemoverReturnTypes120.php @@ -0,0 +1,128 @@ + + * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations + * @license http://www.wtfpl.net/txt/copying/ WTFPL + * @internal + */ +class DefaultArgumentRemoverReturnTypes120 extends InvocationOrder +{ + /** + * @SuppressWarnings(PHPMD) + */ + public function invokedDo(Invocation $invocation): void + { + } + + /** + * @SuppressWarnings(PHPMD) + */ + public function matches(Invocation $invocation) : bool + { + $iClass = class_exists(Invocation::class); + + if ($iClass + || $invocation instanceof Invocation\StaticInvocation + ) { + $this->removeDefaultArguments( + $invocation, + $iClass ? Invocation::class : Invocation\StaticInvocation::class + ); + } elseif (!$this->shouldRemoveDefaultArgumentsWithReflection($invocation)) { + MockFunctionGenerator::removeDefaultArguments($invocation->parameters); + } + + return false; + } + + public function verify() : void + { + } + + /** + * This method is not defined in the interface, but used in + * PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers(). + * + * @return boolean + * @see \PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers() + */ + public function hasMatchers() + { + return false; + } + + public function toString() : string + { + return __CLASS__; + } + + /** + * Remove default arguments from StaticInvocation or its children (hack) + * + * @SuppressWarnings(PHPMD) + */ + private function removeDefaultArguments(Invocation $invocation, string $class) + { + if ($this->shouldRemoveDefaultArgumentsWithReflection($invocation)) { + return; + } + + $remover = function () { + MockFunctionGenerator::removeDefaultArguments($this->parameters); + }; + + $remover->bindTo($invocation, $class)(); + } + + /** + * Alternative to remove default arguments from StaticInvocation or its children (hack) + * + * @SuppressWarnings(PHPMD.StaticAccess) + */ + public static function removeDefaultArgumentsWithReflection(Invocation $invocation): Invocation + { + if (!(new self())->shouldRemoveDefaultArgumentsWithReflection($invocation)) { + return $invocation; + } + + $reflection = new ReflectionClass($invocation); + + $reflectionReturnType = $reflection->getProperty('returnType'); + $reflectionReturnType->setAccessible(true); + + $reflectionIsOptional = $reflection->getProperty('isReturnTypeNullable'); + $reflectionIsOptional->setAccessible(true); + + $returnType = $reflectionReturnType->getValue($invocation); + + if ($reflectionIsOptional->getValue($invocation)) { + $returnType = '?' . $returnType; + } + + $parameters = $invocation->parameters(); + MockFunctionGenerator::removeDefaultArguments($parameters); + + return new Invocation( + $invocation->className(), + $invocation->methodName(), + $parameters, + $returnType, + $invocation->object() + ); + } + + protected function shouldRemoveDefaultArgumentsWithReflection(Invocation $invocation) + { + return method_exists($invocation, 'parameters'); + } +} diff --git a/classes/MockObjectProxyReturnTypes120.php b/classes/MockObjectProxyReturnTypes120.php new file mode 100644 index 0000000..f30503e --- /dev/null +++ b/classes/MockObjectProxyReturnTypes120.php @@ -0,0 +1,100 @@ + + * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations + * @license http://www.wtfpl.net/txt/copying/ WTFPL + * @internal + */ +class MockObjectProxyReturnTypes120 implements MockObject +{ + /** + * @var MockObject $mockObject The mock object. + */ + private $mockObject; + + /** + * Inject the subject. + * + * @param MockObject $mockObject The subject. + */ + public function __construct(MockObject $mockObject) + { + $this->mockObject = $mockObject; + } + + /** + * @SuppressWarnings(PHPMD) + */ + // @codingStandardsIgnoreStart + public function __phpunit_getInvocationHandler(): InvocationHandler + { + return $this->mockObject->__phpunit_getInvocationHandler(); + } + + /** + * @SuppressWarnings(PHPMD) + */ + // @codingStandardsIgnoreStart + public function __phpunit_setOriginalObject(object $originalObject) : void + { + // @codingStandardsIgnoreEnd + $this->mockObject->__phpunit_setOriginalObject($originalObject); + } + + /** + * @SuppressWarnings(PHPMD) + */ + // @codingStandardsIgnoreStart + public function __phpunit_verify(bool $unsetInvocationMocker = true) : void + { + // @codingStandardsIgnoreEnd + $this->mockObject->__phpunit_verify($unsetInvocationMocker); + } + + public function expects(InvocationOrder $matcher) : BuilderInvocationMocker + { + return $this->mockObject->expects($matcher)->method(MockDelegateFunctionBuilder::METHOD); + } + + public function method(Constraint|PropertyHook|string $constraint): InvocationStubber + { + return $this->mockObject->method($constraint); + } + + /** + * This method is not part of the contract but was found in + * PHPUnit's mocked_class.tpl.dist. + * + * @SuppressWarnings(PHPMD) + */ + // @codingStandardsIgnoreStart + public function __phpunit_hasMatchers() : bool + { + // @codingStandardsIgnoreEnd + return $this->mockObject->__phpunit_hasMatchers(); + } + + /** + * @SuppressWarnings(PHPMD) + */ + // @codingStandardsIgnoreStart + public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void + { + // @codingStandardsIgnoreEnd + $this->mockObject->__phpunit_setReturnValueGeneration($returnValueGeneration); + } +} diff --git a/classes/PHPMock.php b/classes/PHPMock.php index 543db17..4575504 100644 --- a/classes/PHPMock.php +++ b/classes/PHPMock.php @@ -66,10 +66,12 @@ public function getFunctionMock($namespace, $name) $delegateBuilder->build($name); $builder = $this->getMockBuilder($delegateBuilder->getFullyQualifiedClassName()); - if (is_callable([$builder, 'addMethods'])) { - $builder->addMethods([$name]); + if (is_callable([$builder, 'onlyMethods'])) { + $builder->onlyMethods([MockDelegateFunctionBuilder::METHOD, $name]); + } elseif (is_callable([$builder, 'setMethods'])) { + $builder->setMethods([MockDelegateFunctionBuilder::METHOD, $name]); } - $mock = $builder->getMockForAbstractClass(); + $mock = $builder->getMock(); $this->addMatcher($mock, $name); $functionMockBuilder = new MockBuilder(); @@ -199,8 +201,9 @@ private function prepareCustomTemplates() } $mockMethodClasses = [ - 'PHPUnit\\Framework\\MockObject\\Generator\\MockMethod', - 'PHPUnit\\Framework\\MockObject\\MockMethod', + 'PHPUnit\\Framework\\MockObject\\Generator\\DoubledMethod', // PHPUnit 12 + 'PHPUnit\\Framework\\MockObject\\Generator\\MockMethod', // PHPUnit 10-11 + 'PHPUnit\\Framework\\MockObject\\MockMethod', // PHPUnit 9 ]; foreach ($mockMethodClasses as $mockMethodClass) { diff --git a/composer.json b/composer.json index 9218934..702ebe8 100644 --- a/composer.json +++ b/composer.json @@ -19,8 +19,8 @@ }, "require": { "php": ">=7", - "phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17 || ^11", - "php-mock/php-mock-integration": "^2.3" + "phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17 || ^11 || ^12", + "php-mock/php-mock-integration": "^3.0" }, "require-dev": { "mockery/mockery": "^1.3.6" diff --git a/tests/MockObjectProxyTest.php b/tests/MockObjectProxyTest.php index 482e052..e9f35e4 100644 --- a/tests/MockObjectProxyTest.php +++ b/tests/MockObjectProxyTest.php @@ -4,6 +4,7 @@ use Mockery; use phpmock\integration\MockDelegateFunctionBuilder; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\Builder\InvocationMocker; use PHPUnit\Framework\MockObject\ConfigurableMethod; use PHPUnit\Framework\MockObject\InvocationHandler; @@ -137,6 +138,7 @@ public function testHasMatcher() * @test * @dataProvider provideTestProxiedMethods */ + #[DataProvider('provideTestProxiedMethods')] public function testProxiedMethods($method, array $arguments = [], $expected = null) { $mock = Mockery::mock(MockObject::class);