From 07a05d4399fc4ae7ca2d6fca4faae791282e15ef Mon Sep 17 00:00:00 2001 From: Matthew Peveler Date: Sat, 5 Oct 2019 12:20:27 -0300 Subject: [PATCH] Fixes PHPUnit 8.4 compatibility --- .gitignore | 3 + .travis.yml | 10 ++ autoload.php | 26 +++++- .../DefaultArgumentRemoverReturnTypes84.php | 81 ++++++++++++++++ classes/MockObjectProxyReturnTypes84.php | 93 +++++++++++++++++++ classes/PHPMock.php | 33 +++++-- composer.json | 2 +- tests/MockObjectProxyTest.php | 59 +++++++++--- tests/PHPMockTest.php | 12 +-- 9 files changed, 287 insertions(+), 32 deletions(-) create mode 100644 .gitignore create mode 100644 classes/DefaultArgumentRemoverReturnTypes84.php create mode 100644 classes/MockObjectProxyReturnTypes84.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1816ab1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.phpunit.result.cache +vendor/ +composer.lock diff --git a/.travis.yml b/.travis.yml index 0f53079..9386e57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,8 @@ cache: env: - PHPUNIT_VERSION=dev-master + - PHPUNIT_VERSION=~8.4.0 + - PHPUNIT_VERSION=~8.3.0 - PHPUNIT_VERSION=~8.2.0 - PHPUNIT_VERSION=~8.1.0 - PHPUNIT_VERSION=~8.0.0 @@ -36,6 +38,10 @@ matrix: exclude: - php: 7.1 env: PHPUNIT_VERSION=dev-master + - php: 7.1 + env: PHPUNIT_VERSION=~8.4.0 + - php: 7.1 + env: PHPUNIT_VERSION=~8.3.0 - php: 7.1 env: PHPUNIT_VERSION=~8.2.0 - php: 7.1 @@ -44,6 +50,10 @@ matrix: env: PHPUNIT_VERSION=~8.0.0 - php: 7 env: PHPUNIT_VERSION=dev-master + - php: 7 + env: PHPUNIT_VERSION=~8.4.0 + - php: 7 + env: PHPUNIT_VERSION=~8.3.0 - php: 7 env: PHPUNIT_VERSION=~8.2.0 - php: 7 diff --git a/autoload.php b/autoload.php index 5eb1a7e..a560fc1 100644 --- a/autoload.php +++ b/autoload.php @@ -1,6 +1,12 @@ = 0 +) { + class_alias(\phpmock\phpunit\DefaultArgumentRemoverReturnTypes84::class, \phpmock\phpunit\DefaultArgumentRemover::class); + class_alias(\phpmock\phpunit\MockObjectProxyReturnTypes84::class, \phpmock\phpunit\MockObjectProxy::class); +} elseif (class_exists(\PHPUnit\Runner\Version::class) && version_compare(\PHPUnit\Runner\Version::id(), '8.1.0') >= 0 ) { class_alias(\phpmock\phpunit\DefaultArgumentRemoverReturnTypes::class, \phpmock\phpunit\DefaultArgumentRemover::class); diff --git a/classes/DefaultArgumentRemoverReturnTypes84.php b/classes/DefaultArgumentRemoverReturnTypes84.php new file mode 100644 index 0000000..6b5c6cf --- /dev/null +++ b/classes/DefaultArgumentRemoverReturnTypes84.php @@ -0,0 +1,81 @@ + + * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations + * @license http://www.wtfpl.net/txt/copying/ WTFPL + * @internal + */ +class DefaultArgumentRemoverReturnTypes84 extends InvocationOrder +{ + /** + * @SuppressWarnings(PHPMD) + */ + public function invokedDo(Invocation $invocation) + { + } + + /** + * @SuppressWarnings(PHPMD) + */ + public function matches(Invocation $invocation) : bool + { + $iClass = class_exists(Invocation::class); + + if ($invocation instanceof Invocation\StaticInvocation + || $iClass + ) { + $this->removeDefaultArguments( + $invocation, + $iClass ? Invocation::class : Invocation\StaticInvocation::class + ); + } else { + 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) + { + $remover = function () { + MockFunctionGenerator::removeDefaultArguments($this->parameters); + }; + + $remover->bindTo($invocation, $class)(); + } +} diff --git a/classes/MockObjectProxyReturnTypes84.php b/classes/MockObjectProxyReturnTypes84.php new file mode 100644 index 0000000..9cbc51f --- /dev/null +++ b/classes/MockObjectProxyReturnTypes84.php @@ -0,0 +1,93 @@ + + * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations + * @license http://www.wtfpl.net/txt/copying/ WTFPL + * @internal + */ +class MockObjectProxyReturnTypes84 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($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); + } + + /** + * 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 603fbc6..9adbfd1 100644 --- a/classes/PHPMock.php +++ b/classes/PHPMock.php @@ -50,25 +50,38 @@ public function getFunctionMock($namespace, $name) { $delegateBuilder = new MockDelegateFunctionBuilder(); $delegateBuilder->build($name); - - $mock = $this->getMockBuilder($delegateBuilder->getFullyQualifiedClassName())->getMockForAbstractClass(); - - $mock->__phpunit_getInvocationMocker()->addMatcher(new DefaultArgumentRemover()); - + + $builder = $this->getMockBuilder($delegateBuilder->getFullyQualifiedClassName()); + if (is_callable([$builder, 'addMethods'])) { + $builder->addMethods([$name]); + } + $mock = $builder->getMockForAbstractClass(); + $this->addMatcher($mock, $name); + $functionMockBuilder = new MockBuilder(); $functionMockBuilder->setNamespace($namespace) ->setName($name) ->setFunctionProvider($mock); - + $functionMock = $functionMockBuilder->build(); $functionMock->enable(); - + $this->registerForTearDown($functionMock); - + $proxy = new MockObjectProxy($mock); return $proxy; } - + + private function addMatcher($mock, $name) + { + if (is_callable([$mock, '__phpunit_getInvocationHandler'])) { + $mocker = $mock->__phpunit_getInvocationHandler()->expects(new DefaultArgumentRemover()); + $mocker->method($name); + return; + } + $mock->__phpunit_getInvocationMocker()->addMatcher(new DefaultArgumentRemover()); + } + /** * Automatically disable function mocks after the test was run. * @@ -83,7 +96,7 @@ public function registerForTearDown(Deactivatable $deactivatable) $result = $this->getTestResultObject(); $result->addListener(new MockDisabler($deactivatable)); } - + /** * Defines the mocked function in the given namespace. * diff --git a/composer.json b/composer.json index cfc2c6a..55cf224 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ }, "require": { "php": ">=7", - "phpunit/phpunit": "^6 || ^7 || ^8", + "phpunit/phpunit": "8.4.x-dev", "php-mock/php-mock-integration": "^2" }, "archive": { diff --git a/tests/MockObjectProxyTest.php b/tests/MockObjectProxyTest.php index 244ab67..41ace7e 100644 --- a/tests/MockObjectProxyTest.php +++ b/tests/MockObjectProxyTest.php @@ -5,6 +5,7 @@ use phpmock\integration\MockDelegateFunctionBuilder; use PHPUnit\Framework\MockObject\Builder\InvocationMocker; use PHPUnit\Framework\MockObject\ConfigurableMethod; +use PHPUnit\Framework\MockObject\InvocationHandler; use PHPUnit\Framework\MockObject\Matcher\Invocation; use PHPUnit\Framework\MockObject\Matcher\MethodName; use PHPUnit\Framework\MockObject\MockObject; @@ -40,11 +41,18 @@ public function testExpects() ) : [MockDelegateFunctionBuilder::METHOD]; - $invocationMocker = new InvocationMocker( - $this->prophesize(MatcherCollection::class)->reveal(), - $this->prophesize(Invocation::class)->reveal(), - $methods - ); + if (class_exists(\PHPUnit\Runner\Version::class) + && version_compare(\PHPUnit\Runner\Version::id(), '8.4.0') >= 0 + ) { + $invocationHandler = new InvocationHandler([$methods], false); + $invocationMocker = $invocationHandler->expects($matcher); + } else { + $invocationMocker = new InvocationMocker( + $this->prophesize(MatcherCollection::class)->reveal(), + $this->prophesize(Invocation::class)->reveal(), + $methods + ); + } $prophecy = $this->prophesize(MockObject::class); $prophecy->expects($matcher)->willReturn($invocationMocker); @@ -57,11 +65,25 @@ public function testExpects() $this->assertSame( (new MethodName(MockDelegateFunctionBuilder::METHOD))->toString(), - ($invocationMocker->getMatcher()->methodNameMatcher - ?? $invocationMocker->getMatcher()->getMethodNameMatcher())->toString() + $this->getMethodMatcher($invocationMocker)->toString() ); } + private function getMethodMatcher($invocationMocker) + { + if (class_exists(\PHPUnit\Runner\Version::class) + && version_compare(\PHPUnit\Runner\Version::id(), '8.4.0') >= 0 + ) { + $reflection = new \ReflectionClass(InvocationMocker::class); + $property = $reflection->getProperty('matcher'); + $property->setAccessible(true); + return $property->getValue($invocationMocker)->getMethodNameRule(); + } + + return $invocationMocker->getMatcher()->methodNameMatcher ?? + $invocationMocker->getMatcher()->getMethodNameMatcher(); + } + /** * Tests delegation of __phpunit_hasMatchers(). * @@ -116,13 +138,26 @@ public function testProxiedMethods($method, array $arguments = [], $expected = n * Returns the test cases for testProxiedMethods(). * * @return array Test cases. + * + * @SuppressWarnings(PHPMD) */ public function provideTestProxiedMethods() { - return [ - ['__phpunit_getInvocationMocker', [], new \PHPUnit\Framework\MockObject\InvocationMocker([], true)], - ['__phpunit_setOriginalObject', ['bar']], - ['__phpunit_verify', [true]], - ]; + $return = []; + if (class_exists(\PHPUnit\Runner\Version::class) + && version_compare(\PHPUnit\Runner\Version::id(), '8.4.0') >= 0 + ) { + $return[] = ['__phpunit_getInvocationHandler', [], new InvocationHandler([], false)]; + } else { + $return[] = [ + '__phpunit_getInvocationMocker', + [], + new \PHPUnit\Framework\MockObject\InvocationMocker([], true) + ]; + } + + $return[] = ['__phpunit_setOriginalObject', ['bar']]; + $return[] = ['__phpunit_verify', [true]]; + return $return; } } diff --git a/tests/PHPMockTest.php b/tests/PHPMockTest.php index 88d7207..0cfef49 100644 --- a/tests/PHPMockTest.php +++ b/tests/PHPMockTest.php @@ -17,22 +17,22 @@ class PHPMockTest extends AbstractMockTest { use PHPMock; - + protected function defineFunction($namespace, $functionName) { self::defineFunctionMock($namespace, $functionName); } - + protected function mockFunction($namespace, $functionName, callable $function) { $mock = $this->getFunctionMock($namespace, $functionName); $mock->expects($this->any())->willReturnCallback($function); } - + protected function disableMocks() { } - + /** * Tests building a mock with arguments. * @@ -42,10 +42,10 @@ public function testFunctionMockWithArguments() { $time = $this->getFunctionMock(__NAMESPACE__, "sqrt"); $time->expects($this->once())->with(9)->willReturn(2); - + $this->assertEquals(2, sqrt(9)); } - + /** * Tests failing an expectation. *