Skip to content

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

Closed
@sebastianbergmann

Description

@sebastianbergmann
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.

Activity

sebastianbergmann

sebastianbergmann commented on Aug 1, 2021

@sebastianbergmann
OwnerAuthor

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.

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"[/+] on Aug 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @sebastianbergmann

      Issue actions

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