Skip to content

Commit

Permalink
Closes #4480
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastianbergmann committed Oct 6, 2020
1 parent 0a0ac90 commit 609d52d
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 1 deletion.
1 change: 1 addition & 0 deletions .php_cs.dist
Expand Up @@ -19,6 +19,7 @@ $finder = PhpCsFixer\Finder::create()
->notName('*.phpt')
->notName('ClassWithAllPossibleReturnTypes.php')
->notName('ClassWithUnionReturnTypes.php')
->notName('ClassWithStaticReturnTypes.php')
->notName('ValueObjectWithEqualsMethodWithUnionReturnType.php')
->notName('ValueObjectWithEqualsMethodThatHasUnionParameterType.php');

Expand Down
7 changes: 7 additions & 0 deletions ChangeLog-9.4.md
Expand Up @@ -2,6 +2,12 @@

All notable changes of the PHPUnit 9.4 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles.

## [9.4.1] - 2020-MM-DD

### Fixed

* [#4480](https://github.com/sebastianbergmann/phpunit/issues/4480): Methods with "static" return type (introduced in PHP 8) are not handled correctly by test double code generator

## [9.4.0] - 2020-10-02

### Added
Expand All @@ -14,4 +20,5 @@ All notable changes of the PHPUnit 9.4 release series are documented in this fil

* The PHPUnit XML configuration generator (that is invoked using the `--generate-configuration` CLI option) now asks for a cache directory (default: `.phpunit.cache`)

[9.4.1]: https://github.com/sebastianbergmann/phpunit/compare/9.4.0...9.4
[9.4.0]: https://github.com/sebastianbergmann/phpunit/compare/9.3.11...9.4.0
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -47,7 +47,7 @@
"sebastian/global-state": "^5.0.1",
"sebastian/object-enumerator": "^4.0.3",
"sebastian/resource-operations": "^3.0.3",
"sebastian/type": "^2.2.2",
"sebastian/type": "^2.3",
"sebastian/version": "^3.0.2"
},
"require-dev": {
Expand Down
5 changes: 5 additions & 0 deletions src/Framework/MockObject/Invocation.php
Expand Up @@ -11,12 +11,14 @@

use function array_map;
use function explode;
use function get_class;
use function implode;
use function is_object;
use function sprintf;
use function strpos;
use function strtolower;
use function substr;
use Doctrine\Instantiator\Instantiator;
use PHPUnit\Framework\SelfDescribing;
use PHPUnit\Util\Type;
use SebastianBergmann\Exporter\Exporter;
Expand Down Expand Up @@ -151,6 +153,9 @@ public function generateReturnValue()
case 'array':
return [];

case 'static':
return (new Instantiator)->instantiate(get_class($this->object));

case 'object':
return new stdClass;

Expand Down
26 changes: 26 additions & 0 deletions tests/_files/ClassWithStaticReturnTypes.php
@@ -0,0 +1,26 @@
<?php declare(strict_types=1);
/*
* This file is part of PHPUnit.
*
* (c) Sebastian Bergmann <sebastian@phpunit.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace PHPUnit\TestFixture;

class ClassWithStaticReturnTypes
{
public function returnsStatic(): static
{
}

public function returnsStaticOrNull(): ?static
{
}

public function returnsUnionWithStatic(): static|\stdClass
{
}
}
@@ -0,0 +1,112 @@
--TEST--
\PHPUnit\Framework\MockObject\Generator::generate('Foo', [], 'MockFoo', true, true)
--SKIPIF--
<?php declare(strict_types=1);
if (PHP_MAJOR_VERSION < 8) {
print 'skip: PHP 8 is required.';
}
--FILE--
<?php declare(strict_types=1);
class ClassWithStaticReturnTypes
{
public function returnsStatic(): static
{
}

public function returnsStaticOrNull(): ?static
{
}

public function returnsUnionWithStatic(): static|\stdClass
{
}
}

require __DIR__ . '/../../../../vendor/autoload.php';

$generator = new \PHPUnit\Framework\MockObject\Generator;

$mock = $generator->generate(
'ClassWithStaticReturnTypes',
[],
'MockClassWithStaticReturnTypes',
true,
true
);

print $mock->getClassCode();
--EXPECTF--
declare(strict_types=1);

class MockClassWithStaticReturnTypes extends ClassWithStaticReturnTypes implements PHPUnit\Framework\MockObject\MockObject
{
use \PHPUnit\Framework\MockObject\Api;
use \PHPUnit\Framework\MockObject\Method;
use \PHPUnit\Framework\MockObject\MockedCloneMethod;

public function returnsStatic(): static
{
$__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(
'ClassWithStaticReturnTypes', 'returnsStatic', $__phpunit_arguments, 'static', $this, true
)
);

return $__phpunit_result;
}

public function returnsStaticOrNull(): ?static
{
$__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(
'ClassWithStaticReturnTypes', 'returnsStaticOrNull', $__phpunit_arguments, '?static', $this, true
)
);

return $__phpunit_result;
}

public function returnsUnionWithStatic(): static|stdClass
{
$__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(
'ClassWithStaticReturnTypes', 'returnsUnionWithStatic', $__phpunit_arguments, 'static|stdClass', $this, true
)
);

return $__phpunit_result;
}
}
35 changes: 35 additions & 0 deletions tests/unit/Framework/MockObject/MockObjectTest.php
Expand Up @@ -9,13 +9,15 @@
*/
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\TestCase;
use PHPUnit\TestFixture\AbstractTrait;
use PHPUnit\TestFixture\AnInterface;
use PHPUnit\TestFixture\ClassThatImplementsSerializable;
use PHPUnit\TestFixture\ClassWithAllPossibleReturnTypes;
use PHPUnit\TestFixture\ClassWithSelfTypeHint;
use PHPUnit\TestFixture\ClassWithStaticMethod;
use PHPUnit\TestFixture\ClassWithStaticReturnTypes;
use PHPUnit\TestFixture\ClassWithUnionReturnTypes;
use PHPUnit\TestFixture\ExampleTrait;
use PHPUnit\TestFixture\InterfaceWithStaticMethod;
Expand Down Expand Up @@ -1135,6 +1137,39 @@ public function testMixedReturnTypeIsDoubledCorrectly(): void
$this->assertNull($stub->returnsMixed());
}

/**
* @requires PHP > 8.0
*/
public function testStaticReturnTypeIsDoubledCorrectly(): void
{
/** @var ClassWithStaticReturnTypes|Stub $stub */
$stub = $this->createStub(ClassWithStaticReturnTypes::class);

$this->assertInstanceOf(ClassWithStaticReturnTypes::class, $stub->returnsStatic());
}

/**
* @requires PHP > 8.0
*/
public function testUnionReturnTypeWithStaticIsDoubledCorrectly(): void
{
/** @var ClassWithStaticReturnTypes|Stub $stub */
$stub = $this->createStub(ClassWithStaticReturnTypes::class);

$this->assertInstanceOf(ClassWithStaticReturnTypes::class, $stub->returnsUnionWithStatic());
}

/**
* @requires PHP > 8.0
*/
public function testNullableStaticReturnTypeIsDoubledCorrectly(): void
{
/** @var ClassWithStaticReturnTypes|Stub $stub */
$stub = $this->createStub(ClassWithStaticReturnTypes::class);

$this->assertNull($stub->returnsStaticOrNull());
}

public function testTraitCanBeDoubled(): void
{
$object = $this->getObjectForTrait(ExampleTrait::class);
Expand Down

0 comments on commit 609d52d

Please sign in to comment.