Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TestCase::getMockFromWsdl() does not work with PHP 8.1-dev #4694

Closed
sebastianbergmann opened this issue Jun 4, 2021 · 5 comments
Closed
Assignees
Labels
feature/test-doubles Test Stubs and Mock Objects type/change-in-php-requires-adaptation A change in PHP requires a change so that existing PHPUnit functionality continues to work

Comments

@sebastianbergmann
Copy link
Owner

With a clean build of PHP 8.1-dev (d2e5203865) I get

php-81 ./phpunit --filter testCreateMockFromWsdl
PHPUnit 8.5.15-41-ge760aee1f by Sebastian Bergmann and contributors.

Runtime:       PHP 8.1.0-dev
Configuration: /usr/local/src/phpunit/phpunit.xml


Fatal error: During inheritance of SoapClient: Uncaught Declaration of Mock_WsdlMock_a4f861c0::__call(string $name, array $args) should be compatible with SoapClient::__call(string $name, array $args): mixed

/usr/local/src/phpunit/src/Framework/MockObject/MockClass.php:45
/usr/local/src/phpunit/src/Framework/MockObject/Generator.php:625
/usr/local/src/phpunit/src/Framework/MockObject/Generator.php:217
/usr/local/src/phpunit/src/Framework/TestCase.php:1861
/usr/local/src/phpunit/tests/unit/Framework/MockObject/MockObjectTest.php:910
/usr/local/src/phpunit/src/Framework/TestCase.php:1472
/usr/local/src/phpunit/src/Framework/TestCase.php:1092
/usr/local/src/phpunit/src/Framework/TestResult.php:703
/usr/local/src/phpunit/src/Framework/TestCase.php:820
/usr/local/src/phpunit/src/Framework/TestSuite.php:627
/usr/local/src/phpunit/src/Framework/TestSuite.php:627
/usr/local/src/phpunit/src/Framework/TestSuite.php:627
/usr/local/src/phpunit/src/TextUI/TestRunner.php:656
/usr/local/src/phpunit/src/TextUI/Command.php:236
/usr/local/src/phpunit/src/TextUI/Command.php:195
 in /usr/local/src/phpunit/src/Framework/MockObject/MockClass.php(45) : eval()'d code on line 3

The generated code that causes the error looks like so:

<?php declare(strict_types=1);

class Mock_WsdlMock_1ef6ee15 extends WsdlMock implements PHPUnit\Framework\MockObject\MockObject
{
    use \PHPUnit\Framework\MockObject\Api;
    use \PHPUnit\Framework\MockObject\Method;
    use \PHPUnit\Framework\MockObject\MockedCloneMethod;

    public function doGoogleSearch($key, $q, $start, $maxResults, $filter, $restrict, $safeSearch, $lr, $ie, $oe)
    {
        $__phpunit_arguments = [$key, $q, $start, $maxResults, $filter, $restrict, $safeSearch, $lr, $ie, $oe];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 10) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 10; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'WsdlMock', 'doGoogleSearch', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function doGetCachedPage($key, $url)
    {
        $__phpunit_arguments = [$key, $url];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 2) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 2; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'WsdlMock', 'doGetCachedPage', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function doSpellingSuggestion($key, $phrase)
    {
        $__phpunit_arguments = [$key, $phrase];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 2) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 2; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'WsdlMock', 'doSpellingSuggestion', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __call(string $name, array $args)
    {
        $__phpunit_arguments = [$name, $args];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 2) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 2; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__call', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __soapCall(string $name, array $args, ?array $options = NULL, $inputHeaders = NULL, &$outputHeaders = NULL)
    {
        $__phpunit_arguments = [$name, $args, $options, $inputHeaders, &$outputHeaders];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 5) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 5; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__soapCall', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __getFunctions()
    {
        $__phpunit_arguments = [];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 0) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__getFunctions', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __getTypes()
    {
        $__phpunit_arguments = [];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 0) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__getTypes', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __getLastRequest()
    {
        $__phpunit_arguments = [];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 0) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__getLastRequest', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __getLastResponse()
    {
        $__phpunit_arguments = [];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 0) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__getLastResponse', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __getLastRequestHeaders()
    {
        $__phpunit_arguments = [];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 0) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__getLastRequestHeaders', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __getLastResponseHeaders()
    {
        $__phpunit_arguments = [];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 0) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__getLastResponseHeaders', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __doRequest(string $request, string $location, string $action, int $version, bool $oneWay = false)
    {
        $__phpunit_arguments = [$request, $location, $action, $version, $oneWay];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 5) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 5; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__doRequest', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __setCookie(string $name, ?string $value = NULL)
    {
        $__phpunit_arguments = [$name, $value];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 2) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 2; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__setCookie', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __getCookies()
    {
        $__phpunit_arguments = [];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 0) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 0; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__getCookies', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __setSoapHeaders($headers = NULL)
    {
        $__phpunit_arguments = [$headers];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 1) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__setSoapHeaders', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }

    public function __setLocation(?string $location = NULL)
    {
        $__phpunit_arguments = [$location];
        $__phpunit_count     = func_num_args();

        if ($__phpunit_count > 1) {
            $__phpunit_arguments_tmp = func_get_args();

            for ($__phpunit_i = 1; $__phpunit_i < $__phpunit_count; $__phpunit_i++) {
                $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i];
            }
        }

        $__phpunit_result = $this->__phpunit_getInvocationHandler()->invoke(
            new \PHPUnit\Framework\MockObject\Invocation(
                'SoapClient', '__setLocation', $__phpunit_arguments, '', $this, true
            )
        );

        return $__phpunit_result;
    }
}

This error does not happen when I revert the changes introduced in php/php-src@8ae4b56.

As far as I understand it, the aforementioned commit is related to the Add return type declarations for internal methods RFC. This RFC says

As the tentative return type declarations in question wouldn't be enforced in PHP 8 versions, ReflectionMethod::hasReturnType() and ReflectionMethod::getReturnType() won't take these into account until PHP 9.0.

@kocsismate Can you please clarify whether or not this error is the result of a bug that was introduced in php/php-src@8ae4b56 or whether PHPUnit needs to be adapted to these changes? Thanks!

@sebastianbergmann sebastianbergmann added type/bug Something is broken feature/test-doubles Test Stubs and Mock Objects type/change-in-php-requires-adaptation A change in PHP requires a change so that existing PHPUnit functionality continues to work and removed type/bug Something is broken labels Jun 4, 2021
@kocsismate
Copy link

kocsismate commented Jun 4, 2021

I think the linked commit itself is correct, however I'm wondering if you have an exception handler setup which converts notices to an exception?

Nikita made a few follow-up fixes so that inheritance notices can be converted to an exception: php/php-src@100a1e8

In any case, it's definitely a good idea to either add the necessary return types or use the ReturnTypeWillChange attribute.

@sebastianbergmann
Copy link
Owner Author

I'm wondering if you have an exception handler setup which converts notices to an exception?

Yes, PHPUnit registers an error handler that converts notices etc. to exceptions.

@sebastianbergmann sebastianbergmann self-assigned this Jun 4, 2021
@nikic
Copy link

nikic commented Jun 4, 2021

For the purpose of mock object generation, you should be able to basically do $rf->getReturnType() ?? $rf->getTentativeReturnType() (modulo version check). I believe for mocks it's fine to just always generate the tentative type as a proper type.

@sebastianbergmann
Copy link
Owner Author

Thanks, Nikita. Already did that locally. However, additional changes are required as PHPUnit 8.5 does not know about mixed.

@nikic
Copy link

nikic commented Jun 4, 2021

Ah sorry, I didn't notice this is for PHPUnit 8. For that version it may be better to generate #[ReturnTypeWillChange] attributes, otherwise you'd have to support the full PHP 8 typesystem, which I assume you don't for that version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature/test-doubles Test Stubs and Mock Objects type/change-in-php-requires-adaptation A change in PHP requires a change so that existing PHPUnit functionality continues to work
Projects
None yet
Development

No branches or pull requests

3 participants