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

Automatic return value generation leads to invalid (and superfluous) test double code generation when a stubbed method returns "*|false" #4750

Closed
sebastianbergmann opened this issue Aug 1, 2021 · 1 comment
Assignees
Labels
feature/test-doubles Test Stubs and Mock Objects type/bug Something is broken

Comments

@sebastianbergmann
Copy link
Owner

@mbabker tweeted about an issue with PHPUnit 9.5, PHP 8.1, and test doubles. He posted a reproducing example here. While this example is run, the following code is generated:

declare(strict_types=1);

class Mock_PDO_485bed6b extends PDO implements PHPUnit\Framework\MockObject\MockObject
{
    use \PHPUnit\Framework\MockObject\Api;
    use \PHPUnit\Framework\MockObject\Method;
    use \PHPUnit\Framework\MockObject\MockedCloneMethod;

    public function beginTransaction(): bool
    {
        $__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(
                'PDO', 'beginTransaction', $__phpunit_arguments, 'bool', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function commit(): bool
    {
        $__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(
                'PDO', 'commit', $__phpunit_arguments, 'bool', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function errorCode(): ?string
    {
        $__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(
                'PDO', 'errorCode', $__phpunit_arguments, '?string', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function errorInfo(): array
    {
        $__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(
                'PDO', 'errorInfo', $__phpunit_arguments, 'array', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function exec(string $statement): false|int
    {
        $__phpunit_arguments = [$statement];
        $__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(
                'PDO', 'exec', $__phpunit_arguments, 'false|int', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function getAttribute(int $attribute): mixed
    {
        $__phpunit_arguments = [$attribute];
        $__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(
                'PDO', 'getAttribute', $__phpunit_arguments, 'mixed', $this, false
            )
        );

        return $__phpunit_result;
    }

    public static function getAvailableDrivers(): array
    {
        throw new \PHPUnit\Framework\MockObject\BadMethodCallException('Static method "getAvailableDrivers" cannot be invoked on mock object');
    }

    public function inTransaction(): bool
    {
        $__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(
                'PDO', 'inTransaction', $__phpunit_arguments, 'bool', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function lastInsertId(?string $name = NULL): false|string
    {
        $__phpunit_arguments = [$name];
        $__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(
                'PDO', 'lastInsertId', $__phpunit_arguments, 'false|string', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function prepare(string $query, array $options = array ()): PDOStatement|false
    {
        $__phpunit_arguments = [$query, $options];
        $__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(
                'PDO', 'prepare', $__phpunit_arguments, 'PDOStatement|false', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function query(string $query, ?int $fetchMode = NULL, mixed ...$fetchModeArgs): PDOStatement|false
    {
        $__phpunit_arguments = [$query, $fetchMode];
        $__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(
                'PDO', 'query', $__phpunit_arguments, 'PDOStatement|false', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function quote(string $string, int $type = 3): false|string
    {
        $__phpunit_arguments = [$string, $type];
        $__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(
                'PDO', 'quote', $__phpunit_arguments, 'false|string', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function rollBack(): bool
    {
        $__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(
                'PDO', 'rollBack', $__phpunit_arguments, 'bool', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function setAttribute(int $attribute, $value): bool
    {
        $__phpunit_arguments = [$attribute, $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(
                'PDO', 'setAttribute', $__phpunit_arguments, 'bool', $this, false
            )
        );

        return $__phpunit_result;
    }
}
declare(strict_types=1);

class PDOStatement|false
{
}
class Mock_PDOStatement|false_8ffdc1db extends PDOStatement|false implements PHPUnit\Framework\MockObject\MockObject
{
    use \PHPUnit\Framework\MockObject\Api;
    use \PHPUnit\Framework\MockObject\Method;
    use \PHPUnit\Framework\MockObject\MockedCloneMethod;
}

PDOStatement|false and Mock_PDOStatement|false_8ffdc1db are obviously invalid class names.

@sebastianbergmann sebastianbergmann added type/bug Something is broken feature/test-doubles Test Stubs and Mock Objects labels Aug 1, 2021
@sebastianbergmann sebastianbergmann self-assigned this Aug 1, 2021
@sebastianbergmann
Copy link
Owner Author

sebastianbergmann commented Aug 1, 2021

PHPUnit 10.0-dev correctly generates only one test double class:

declare(strict_types=1);

class Mock_PDO_35d4bc58 extends PDO implements PHPUnit\Framework\MockObject\MockObject
{
    use \PHPUnit\Framework\MockObject\Api;
    use \PHPUnit\Framework\MockObject\Method;
    use \PHPUnit\Framework\MockObject\MockedCloneMethod;

    public function beginTransaction(): bool
    {
        $__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(
                'PDO', 'beginTransaction', $__phpunit_arguments, 'bool', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function commit(): bool
    {
        $__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(
                'PDO', 'commit', $__phpunit_arguments, 'bool', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function errorCode(): ?string
    {
        $__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(
                'PDO', 'errorCode', $__phpunit_arguments, '?string', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function errorInfo(): array
    {
        $__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(
                'PDO', 'errorInfo', $__phpunit_arguments, 'array', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function exec(string $statement): false|int
    {
        $__phpunit_arguments = [$statement];
        $__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(
                'PDO', 'exec', $__phpunit_arguments, 'false|int', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function getAttribute(int $attribute): mixed
    {
        $__phpunit_arguments = [$attribute];
        $__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(
                'PDO', 'getAttribute', $__phpunit_arguments, 'mixed', $this, false
            )
        );

        return $__phpunit_result;
    }

    public static function getAvailableDrivers(): array
    {
        throw new \PHPUnit\Framework\MockObject\BadMethodCallException('Static method "getAvailableDrivers" cannot be invoked on mock object');
    }

    public function inTransaction(): bool
    {
        $__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(
                'PDO', 'inTransaction', $__phpunit_arguments, 'bool', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function lastInsertId(?string $name = NULL): false|string
    {
        $__phpunit_arguments = [$name];
        $__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(
                'PDO', 'lastInsertId', $__phpunit_arguments, 'false|string', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function prepare(string $query, array $options = array ()): PDOStatement|false
    {
        $__phpunit_arguments = [$query, $options];
        $__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(
                'PDO', 'prepare', $__phpunit_arguments, 'PDOStatement|false', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function query(string $query, ?int $fetchMode = NULL, mixed ...$fetchModeArgs): PDOStatement|false
    {
        $__phpunit_arguments = [$query, $fetchMode];
        $__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(
                'PDO', 'query', $__phpunit_arguments, 'PDOStatement|false', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function quote(string $string, int $type = 3): false|string
    {
        $__phpunit_arguments = [$string, $type];
        $__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(
                'PDO', 'quote', $__phpunit_arguments, 'false|string', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function rollBack(): bool
    {
        $__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(
                'PDO', 'rollBack', $__phpunit_arguments, 'bool', $this, false
            )
        );

        return $__phpunit_result;
    }

    public function setAttribute(int $attribute, $value): bool
    {
        $__phpunit_arguments = [$attribute, $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(
                'PDO', 'setAttribute', $__phpunit_arguments, 'bool', $this, false
            )
        );

        return $__phpunit_result;
    }
}

The default return value generator correctly returns false for PDOStatement|false and does not try to generate code for a stub for PDOStatement|false.

@sebastianbergmann sebastianbergmann changed the title "false" pseudo type leads to invalid test double code being generated Automatic return value generation leads to invalid (and superfluous) test double code generation when a stubbed method returns "*|false" Aug 1, 2021
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/bug Something is broken
Projects
None yet
Development

No branches or pull requests

1 participant