Skip to content

Commit

Permalink
Fix mocking anonymous classes (#1415)
Browse files Browse the repository at this point in the history
Signed-off-by: Nathanael Esayeas <nathanael.esayeas@protonmail.com>
  • Loading branch information
ghostwriter committed Apr 29, 2024
1 parent d2fcf80 commit 6db8c48
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 67 deletions.
52 changes: 28 additions & 24 deletions library/Mockery/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace Mockery;

use Closure;
use Exception as PHPException;
use Mockery;
use Mockery\Exception\InvalidOrderException;
Expand Down Expand Up @@ -208,7 +209,7 @@ static function ($name): bool {
*
* @template TMock of LegacyMockInterface&MockInterface&object
*
* @param array|string ...$args
* @param array|string[]|Closure ...$args
*
* @throws ReflectionException|RuntimeException
*
Expand Down Expand Up @@ -261,7 +262,7 @@ public function mock(...$args)
continue;
}

if (strpos($type, ',') && (strpos($type, ']') === 0 || strpos($type, ']') === false)) {
if (strpos($type, ',') && !strpos($type, ']')) {
$interfaces = explode(',', str_replace(' ', '', $type));

$builder->addTargets($interfaces);
Expand Down Expand Up @@ -293,10 +294,7 @@ public function mock(...$args)

$class = $parts[0];

if (
! class_exists($class, true)
&& ! interface_exists($class, true)
) {
if (! class_exists($class, true) && ! interface_exists($class, true)) {
throw new Exception('Can only create a partial mock from an existing class or interface');
}

Expand All @@ -319,15 +317,7 @@ public function mock(...$args)
continue;
}

if (! $this->isValidClassName($type)) {
throw new Exception('Class name contains invalid characters');
}

if (
class_exists($type, true)
|| interface_exists($type, true)
|| trait_exists($type, true)
) {
if (class_exists($type, true) || interface_exists($type, true) || trait_exists($type, true)) {
$builder->addTarget($type);

continue;
Expand All @@ -337,6 +327,10 @@ class_exists($type, true)
throw new Exception(sprintf("Mockery can't find '%s' so can't mock it", $type));
}

if (! $this->isValidClassName($type)) {
throw new Exception('Class name contains invalid characters');
}

$builder->addTarget($type);

// unions are "sum" types and not "intersections", and so we must only process the first part
Expand All @@ -352,18 +346,28 @@ class_exists($type, true)
continue;
}

if ($arg !== [] && array_keys($arg) !== range(0, count($arg) - 1)) {
// if associative array
if (array_key_exists(self::BLOCKS, $arg)) {
$blocks = $arg[self::BLOCKS];
}
if (is_array($arg)) {
if ([] !== $arg && array_keys($arg) !== range(0, count($arg) - 1)) {
// if associative array
if (array_key_exists(self::BLOCKS, $arg)) {
$blocks = $arg[self::BLOCKS];
}

unset($arg[self::BLOCKS]);
unset($arg[self::BLOCKS]);

$quickDefinitions = $arg;

continue;
}

$quickDefinitions = $arg;
} else {
$constructorArgs = $arg;

continue;
}

throw new Exception(sprintf(
'Unable to parse arguments sent to %s::mock()', get_class($this)
));
}

$builder->addBlackListedMethods($blocks);
Expand Down Expand Up @@ -405,7 +409,7 @@ class_exists($type, true)
}
}

if ($expectationClosure !== null) {
if ($expectationClosure instanceof Closure) {
$expectationClosure($mock);
}

Expand Down
65 changes: 31 additions & 34 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -12,50 +12,47 @@
>
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./library/</directory>
<directory suffix=".php">library/</directory>
</include>
<exclude>
<directory>./library/Mockery/Adapter/Phpunit/Legacy</directory>
<file>./library/Mockery/Adapter/Phpunit/TestListener.php</file>
<file>./library/Mockery/Adapter/Phpunit/MockeryPHPUnitIntegrationAssertPostConditions.php</file>
<file>./library/Mockery/Adapter/Phpunit/MockeryTestCaseSetUp.php</file>
<directory>library/Mockery/Adapter/Phpunit/Legacy</directory>
<file>library/Mockery/Adapter/Phpunit/TestListener.php</file>
<file>library/Mockery/Adapter/Phpunit/MockeryPHPUnitIntegrationAssertPostConditions.php</file>
<file>library/Mockery/Adapter/Phpunit/MockeryTestCaseSetUp.php</file>
</exclude>
<report>
<!-- <html outputDirectory=".cache/phpunit/coverage-html"/> -->
<html outputDirectory=".cache/phpunit/coverage-html"/>
<clover outputFile=".cache/phpunit/clover.xml"/>
<text outputFile=".cache/phpunit/coverage.txt"/>
</report>
</coverage>
<testsuites>
<testsuite name="Mockery Test Suite PHP7">
<directory suffix="Test.php">./tests</directory>
<exclude>./tests/PHP80</exclude>
<exclude>./tests/PHP81</exclude>
<exclude>./tests/PHP82</exclude>
<exclude>./tests/Unit/PHP81</exclude>
<exclude>./tests/Unit/PHP82</exclude>
<exclude>./tests/Unit/PHP83</exclude>
<testsuite name="default">
<directory>tests</directory>
<exclude>fixtures</exclude>
<exclude>tests/Fixture</exclude>
<exclude>tests/PHP*</exclude>
<exclude>tests/Unit/PHP*</exclude>
<exclude>tests/Unit/Regression</exclude>
</testsuite>
<testsuite name="Mockery Test Suite PHP80">
<directory phpVersion="8.0.0-dev" phpVersionOperator="&gt;=">./tests</directory>
<exclude>./tests/PHP81</exclude>
<exclude>./tests/PHP82</exclude>
<exclude>./tests/Unit/PHP81</exclude>
<exclude>./tests/Unit/PHP82</exclude>
<exclude>./tests/Unit/PHP83</exclude>
</testsuite>
<testsuite name="Mockery Test Suite PHP81">
<directory phpVersion="8.1.0-dev" phpVersionOperator="&gt;=">./tests</directory>
<exclude>./tests/PHP82</exclude>
<exclude>./tests/Unit/PHP82</exclude>
<exclude>./tests/Unit/PHP83</exclude>
</testsuite>
<testsuite name="Mockery Test Suite PHP82">
<directory phpVersion="8.2.0-dev" phpVersionOperator="&gt;=">./tests</directory>
<exclude>./tests/Unit/PHP83</exclude>
</testsuite>
<testsuite name="Mockery Test Suite PHP83">
<directory phpVersion="8.3.0-dev" phpVersionOperator="&gt;=">./tests</directory>
<testsuite name="versioned">
<!--<directory phpVersion="7.3.0-dev">tests/Unit/*</directory> -->
<!--<directory phpVersion="7.3.0-dev">tests</directory> -->
<!--<directory phpVersion="7.4.0-dev">tests/PHP74</directory>-->
<!--<directory phpVersion="7.4.0-dev">tests/Unit/PHP74</directory>-->
<!--<directory phpVersion="8.0.0-dev">tests/Unit/PHP80</directory>-->
<!--<directory phpVersion="8.3.0-dev">tests/PHP83</directory>-->
<!--<directory phpVersion="8.4.0-dev">tests/PHP84</directory>-->
<!--<directory phpVersion="8.4.0-dev">tests/Unit/PHP84</directory>-->
<directory phpVersion="8.0.0-dev">tests/PHP80</directory>
<directory phpVersion="8.0.0-dev">tests/Unit/Regression</directory>
<directory phpVersion="8.1.0-dev">tests/PHP81</directory>
<directory phpVersion="8.1.0-dev">tests/Unit/PHP81</directory>
<directory phpVersion="8.2.0-dev">tests/PHP82</directory>
<directory phpVersion="8.2.0-dev">tests/Unit/PHP82</directory>
<directory phpVersion="8.3.0-dev">tests/Unit/PHP83</directory>
<exclude>fixtures</exclude>
<exclude>tests/Fixture</exclude>
</testsuite>
</testsuites>
</phpunit>
21 changes: 12 additions & 9 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
<InternalMethod>
<code><![CDATA[Reflector::isReservedWord($type)]]></code>
</InternalMethod>
<InvalidArgument>
<code><![CDATA[$newMockName]]></code>
</InvalidArgument>
<InvalidReturnStatement>
<code><![CDATA[$argument]]></code>
<code><![CDATA[$container->getMocks()[$demeterMockKey] ?? null]]></code>
Expand Down Expand Up @@ -74,8 +77,6 @@
<code><![CDATA[$value]]></code>
</MixedArgument>
<MixedArgumentTypeCoercion>
<code><![CDATA[$args]]></code>
<code><![CDATA[$args]]></code>
<code><![CDATA[$formattedArguments]]></code>
<code><![CDATA[$k]]></code>
</MixedArgumentTypeCoercion>
Expand Down Expand Up @@ -128,6 +129,8 @@
<PossiblyInvalidArgument>
<code><![CDATA[$args]]></code>
<code><![CDATA[$args]]></code>
<code><![CDATA[$args]]></code>
<code><![CDATA[$args]]></code>
</PossiblyInvalidArgument>
<PossiblyNullPropertyAssignmentValue>
<code><![CDATA[null]]></code>
Expand Down Expand Up @@ -373,7 +376,7 @@
<DocblockTypeContradiction>
<code><![CDATA[$arg instanceof MockConfigurationBuilder]]></code>
<code><![CDATA[$match === false]]></code>
<code><![CDATA[is_object($arg)]]></code>
<code><![CDATA[is_string($arg)]]></code>
</DocblockTypeContradiction>
<InvalidArgument>
<code><![CDATA[$keys]]></code>
Expand Down Expand Up @@ -473,7 +476,7 @@
<code><![CDATA[LegacyMockInterface&MockInterface&TMock]]></code>
</MoreSpecificReturnType>
<NoValue>
<code><![CDATA[$expectationClosure]]></code>
<code><![CDATA[$arg]]></code>
</NoValue>
<PossiblyUndefinedIntArrayOffset>
<code><![CDATA[$parts[1]]]></code>
Expand All @@ -488,15 +491,18 @@
<PossiblyUnusedReturnValue>
<code><![CDATA[int]]></code>
</PossiblyUnusedReturnValue>
<RedundantConditionGivenDocblockType>
<code><![CDATA[is_array($arg)]]></code>
</RedundantConditionGivenDocblockType>
<RiskyTruthyFalsyComparison>
<code><![CDATA[!strpos($type, ']')]]></code>
<code><![CDATA[strpos($type, ',')]]></code>
</RiskyTruthyFalsyComparison>
<TooManyArguments>
<code><![CDATA[mockery_init]]></code>
</TooManyArguments>
<TypeDoesNotContainType>
<code><![CDATA[is_callable($finalArg) && is_object($finalArg)]]></code>
<code><![CDATA[is_object($finalArg)]]></code>
<code><![CDATA[$arg === 'null']]></code>
</TypeDoesNotContainType>
<UndefinedInterfaceMethod>
<code><![CDATA[atLeast]]></code>
Expand All @@ -507,9 +513,6 @@
<code><![CDATA[atLeast]]></code>
<code><![CDATA[byDefault]]></code>
</UndefinedMagicMethod>
<UnusedVariable>
<code><![CDATA[$expectationClosure]]></code>
</UnusedVariable>
</file>
<file src="library/Mockery/CountValidator/AtLeast.php">
<InvalidOperand>
Expand Down
25 changes: 25 additions & 0 deletions tests/Unit/Regression/Issue1414Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Mockery\Tests\Unit\Regression;

use Mockery;
use PHPUnit\Framework\TestCase;

/**
* @covers \Mockery
* @see https://github.com/mockery/mockery/issues/1414
* @requires PHP 8.0
*/
final class Issue1414Test extends TestCase
{
public function testMockAnonymousClass(): void
{
$class = new class() extends \stdClass {};

$mock = Mockery::mock($class::class);

self::assertInstanceOf($class::class, $mock);
}
}

0 comments on commit 6db8c48

Please sign in to comment.