Skip to content

Commit cccdd46

Browse files
Closes #6601
1 parent 267bbfd commit cccdd46

9 files changed

Lines changed: 126 additions & 0 deletions

File tree

ChangeLog-12.5.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ All notable changes of the PHPUnit 12.5 release series are documented in this fi
1111
### Fixed
1212

1313
* [#6595](https://github.com/sebastianbergmann/phpunit/issues/6595): Crash when before-class or after-class method fails with assertion failure
14+
* [#6601](https://github.com/sebastianbergmann/phpunit/issues/6601): Anonymous classes are not rejected with a clear error when creating a test double
1415
* `MockBuilder::setMockClassName()` and `TestStubBuilder::setStubClassName()` now reject values that are not valid unqualified PHP class identifiers, throwing the new `InvalidClassNameException`
1516
* The regular expression used by `Generator::ensureValidMethods()` to validate method names passed to `MockBuilder::onlyMethods()` and `addMethods()` was not anchored, so any string containing a valid identifier substring (including strings with parentheses, braces, comments, or newlines) was accepted
1617

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\Framework\MockObject\Generator;
11+
12+
use function sprintf;
13+
14+
/**
15+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
16+
*
17+
* @internal This class is not covered by the backward compatibility promise for PHPUnit
18+
*/
19+
final class ClassIsAnonymousException extends \PHPUnit\Framework\Exception implements Exception
20+
{
21+
public function __construct(string $className)
22+
{
23+
parent::__construct(
24+
sprintf(
25+
'Class "%s" is an anonymous class and cannot be doubled',
26+
$className,
27+
),
28+
);
29+
}
30+
}

src/Framework/MockObject/Generator/Generator.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ final class Generator
8080
* @param ?list<non-empty-string> $methods
8181
* @param array<mixed> $arguments
8282
*
83+
* @throws ClassIsAnonymousException
8384
* @throws ClassIsEnumerationException
8485
* @throws ClassIsFinalException
8586
* @throws DuplicateMethodException
@@ -199,6 +200,7 @@ public function testDoubleForInterfaceIntersection(array $interfaces, bool $mock
199200
* @param class-string $type
200201
* @param ?list<non-empty-string> $methods
201202
*
203+
* @throws ClassIsAnonymousException
202204
* @throws ClassIsEnumerationException
203205
* @throws ClassIsFinalException
204206
* @throws ReflectionException
@@ -337,6 +339,7 @@ private function instantiate(DoubledClass $mockClass, bool $mockObject, bool $ca
337339
* @param class-string $type
338340
* @param ?list<non-empty-string> $explicitMethods
339341
*
342+
* @throws ClassIsAnonymousException
340343
* @throws ClassIsEnumerationException
341344
* @throws ClassIsFinalException
342345
* @throws MethodNamedMethodException
@@ -369,6 +372,10 @@ private function generateCodeForTestDoubleClass(string $type, bool $mockObject,
369372

370373
$class = $this->reflectClass($_mockClassName['fullClassName']);
371374

375+
if ($class->isAnonymous()) {
376+
throw new ClassIsAnonymousException($_mockClassName['fullClassName']);
377+
}
378+
372379
if ($class->isEnum()) {
373380
throw new ClassIsEnumerationException($_mockClassName['fullClassName']);
374381
}

src/Framework/MockObject/MockBuilder.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
use function assert;
1313
use PHPUnit\Framework\InvalidArgumentException;
14+
use PHPUnit\Framework\MockObject\Generator\ClassIsAnonymousException;
1415
use PHPUnit\Framework\MockObject\Generator\ClassIsEnumerationException;
1516
use PHPUnit\Framework\MockObject\Generator\ClassIsFinalException;
1617
use PHPUnit\Framework\MockObject\Generator\DuplicateMethodException;
@@ -49,6 +50,7 @@ public function __construct(TestCase $testCase, string $type)
4950
/**
5051
* Creates a mock object using a fluent interface.
5152
*
53+
* @throws ClassIsAnonymousException
5254
* @throws ClassIsEnumerationException
5355
* @throws ClassIsFinalException
5456
* @throws DuplicateMethodException

src/Framework/MockObject/TestStubBuilder.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace PHPUnit\Framework\MockObject;
1111

1212
use function assert;
13+
use PHPUnit\Framework\MockObject\Generator\ClassIsAnonymousException;
1314
use PHPUnit\Framework\MockObject\Generator\ClassIsEnumerationException;
1415
use PHPUnit\Framework\MockObject\Generator\ClassIsFinalException;
1516
use PHPUnit\Framework\MockObject\Generator\DuplicateMethodException;
@@ -35,6 +36,7 @@ final class TestStubBuilder extends TestDoubleBuilder
3536
/**
3637
* Creates a test stub using a fluent interface.
3738
*
39+
* @throws ClassIsAnonymousException
3840
* @throws ClassIsEnumerationException
3941
* @throws ClassIsFinalException
4042
* @throws DuplicateMethodException
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
https://github.com/sebastianbergmann/phpunit/issues/6601
3+
--FILE--
4+
<?php declare(strict_types=1);
5+
$_SERVER['argv'][] = '--do-not-cache-result';
6+
$_SERVER['argv'][] = '--no-configuration';
7+
$_SERVER['argv'][] = __DIR__ . '/6601/Issue6601Test.php';
8+
9+
require_once __DIR__ . '/../../bootstrap.php';
10+
11+
(new PHPUnit\TextUI\Application)->run($_SERVER['argv']);
12+
--EXPECTF--
13+
PHPUnit %s by Sebastian Bergmann and contributors.
14+
15+
Runtime: %s
16+
17+
E 1 / 1 (100%)
18+
19+
Time: %s, Memory: %s
20+
21+
There was 1 error:
22+
23+
1) PHPUnit\TestFixture\Issue6601\Issue6601Test::testOne
24+
PHPUnit\Framework\MockObject\Generator\ClassIsAnonymousException: Class "PHPUnit\TestFixture\Issue6601\Customizable@anonymous%sIssue6601Test.php:%d%s" is an anonymous class and cannot be doubled
25+
26+
%sIssue6601Test.php:%d
27+
28+
ERRORS!
29+
Tests: 1, Assertions: 0, Errors: 1.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\TestFixture\Issue6601;
11+
12+
use PHPUnit\Framework\TestCase;
13+
use stdClass;
14+
15+
interface Customizable
16+
{
17+
public function customize(): void;
18+
}
19+
20+
final class Issue6601Test extends TestCase
21+
{
22+
public function testOne(): void
23+
{
24+
$object = new class implements Customizable
25+
{
26+
public function customize(?stdClass $custom = null): void
27+
{
28+
}
29+
};
30+
31+
self::createMock($object::class);
32+
}
33+
}

tests/unit/Framework/MockObject/Creation/CreateMockTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PHPUnit\Framework\Attributes\Group;
1313
use PHPUnit\Framework\Attributes\Medium;
1414
use PHPUnit\Framework\Attributes\TestDox;
15+
use PHPUnit\Framework\MockObject\Generator\ClassIsAnonymousException;
1516
use PHPUnit\Framework\MockObject\Generator\ClassIsEnumerationException;
1617
use PHPUnit\Framework\MockObject\Generator\ClassIsFinalException;
1718
use PHPUnit\Framework\MockObject\Generator\UnknownTypeException;
@@ -77,6 +78,16 @@ public function testCannotCreateMockObjectForEnumeration(): void
7778
$this->createMock(Enumeration::class);
7879
}
7980

81+
public function testCannotCreateMockObjectForAnonymousClass(): void
82+
{
83+
$object = new class extends ExtendableClass
84+
{};
85+
86+
$this->expectException(ClassIsAnonymousException::class);
87+
88+
$this->createMock($object::class);
89+
}
90+
8091
public function testCannotCreateMockObjectForUnknownType(): void
8192
{
8293
$this->expectException(UnknownTypeException::class);

tests/unit/Framework/MockObject/Creation/CreateStubTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PHPUnit\Framework\Attributes\Group;
1313
use PHPUnit\Framework\Attributes\Medium;
1414
use PHPUnit\Framework\Attributes\TestDox;
15+
use PHPUnit\Framework\MockObject\Generator\ClassIsAnonymousException;
1516
use PHPUnit\Framework\MockObject\Generator\ClassIsEnumerationException;
1617
use PHPUnit\Framework\MockObject\Generator\ClassIsFinalException;
1718
use PHPUnit\Framework\MockObject\Generator\UnknownTypeException;
@@ -74,6 +75,16 @@ public function testCannotCreateTestStubForEnumeration(): void
7475
$this->createStub(Enumeration::class);
7576
}
7677

78+
public function testCannotCreateTestStubForAnonymousClass(): void
79+
{
80+
$object = new class extends ExtendableClass
81+
{};
82+
83+
$this->expectException(ClassIsAnonymousException::class);
84+
85+
$this->createStub($object::class);
86+
}
87+
7788
public function testCannotCreateTestStubForUnknownType(): void
7889
{
7990
$this->expectException(UnknownTypeException::class);

0 commit comments

Comments
 (0)