Skip to content

Commit

Permalink
The configured name for a test double class may also not be avaiable …
Browse files Browse the repository at this point in the history
…because an interface or a trait of the same name already exists
  • Loading branch information
sebastianbergmann committed Apr 26, 2024
1 parent b7e95f5 commit 63183f2
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 20 deletions.
Expand Up @@ -14,14 +14,17 @@
/**
* @internal This class is not covered by the backward compatibility promise for PHPUnit
*/
final class ClassAlreadyExistsException extends \PHPUnit\Framework\Exception implements Exception
final class NameAlreadyInUseException extends \PHPUnit\Framework\Exception implements Exception
{
public function __construct(string $className)
/**
* @psalm-param class-string|trait-string $name
*/
public function __construct(string $name)
{
parent::__construct(
sprintf(
'Class "%s" already exists',
$className,
'The name "%s" is already in use',
$name,
),
);
}
Expand Down
24 changes: 13 additions & 11 deletions src/Framework/MockObject/Generator/Generator.php
Expand Up @@ -92,12 +92,12 @@ final class Generator
/**
* Returns a test double for the specified class.
*
* @throws ClassAlreadyExistsException
* @throws ClassIsEnumerationException
* @throws ClassIsFinalException
* @throws ClassIsReadonlyException
* @throws DuplicateMethodException
* @throws InvalidMethodNameException
* @throws NameAlreadyInUseException
* @throws OriginalConstructorInvocationRequiredException
* @throws ReflectionException
* @throws RuntimeException
Expand All @@ -114,7 +114,7 @@ public function testDouble(string $type, bool $mockObject, ?array $methods = [],
}

$this->ensureValidMethods($methods);
$this->ensureMockedClassDoesNotAlreadyExist($mockClassName);
$this->ensureNameForTestDoubleClassIsAvailable($mockClassName);

if (!$callOriginalConstructor && $callOriginalMethods) {
throw new OriginalConstructorInvocationRequiredException;
Expand Down Expand Up @@ -219,13 +219,13 @@ public function testDoubleForInterfaceIntersection(array $interfaces, bool $mock
*
* Concrete methods to mock can be specified with the $mockedMethods parameter.
*
* @throws ClassAlreadyExistsException
* @throws ClassIsEnumerationException
* @throws ClassIsFinalException
* @throws ClassIsReadonlyException
* @throws DuplicateMethodException
* @throws InvalidArgumentException
* @throws InvalidMethodNameException
* @throws NameAlreadyInUseException
* @throws OriginalConstructorInvocationRequiredException
* @throws ReflectionException
* @throws RuntimeException
Expand Down Expand Up @@ -279,13 +279,13 @@ interface_exists($originalClassName, $callAutoload)) {
*
* @psalm-param trait-string $traitName
*
* @throws ClassAlreadyExistsException
* @throws ClassIsEnumerationException
* @throws ClassIsFinalException
* @throws ClassIsReadonlyException
* @throws DuplicateMethodException
* @throws InvalidArgumentException
* @throws InvalidMethodNameException
* @throws NameAlreadyInUseException
* @throws OriginalConstructorInvocationRequiredException
* @throws ReflectionException
* @throws RuntimeException
Expand Down Expand Up @@ -922,17 +922,19 @@ private function ensureValidMethods(?array $methods): void
}

/**
* @throws ClassAlreadyExistsException
* @throws NameAlreadyInUseException
* @throws ReflectionException
*/
private function ensureMockedClassDoesNotAlreadyExist(string $mockClassName): void
private function ensureNameForTestDoubleClassIsAvailable(string $className): void
{
if ($mockClassName !== '' && class_exists($mockClassName, false)) {
$reflector = $this->reflectClass($mockClassName);
if ($className === '') {
return;
}

if (!$reflector->implementsInterface(MockObject::class)) {
throw new ClassAlreadyExistsException($mockClassName);
}
if (class_exists($className, false) ||
interface_exists($className, false) ||
trait_exists($className, false)) {
throw new NameAlreadyInUseException($className);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/Framework/MockObject/MockBuilder.php
Expand Up @@ -14,13 +14,13 @@
use function trait_exists;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\InvalidArgumentException;
use PHPUnit\Framework\MockObject\Generator\ClassAlreadyExistsException;
use PHPUnit\Framework\MockObject\Generator\ClassIsEnumerationException;
use PHPUnit\Framework\MockObject\Generator\ClassIsFinalException;
use PHPUnit\Framework\MockObject\Generator\ClassIsReadonlyException;
use PHPUnit\Framework\MockObject\Generator\DuplicateMethodException;
use PHPUnit\Framework\MockObject\Generator\Generator;
use PHPUnit\Framework\MockObject\Generator\InvalidMethodNameException;
use PHPUnit\Framework\MockObject\Generator\NameAlreadyInUseException;
use PHPUnit\Framework\MockObject\Generator\OriginalConstructorInvocationRequiredException;
use PHPUnit\Framework\MockObject\Generator\ReflectionException;
use PHPUnit\Framework\MockObject\Generator\RuntimeException;
Expand Down Expand Up @@ -76,13 +76,13 @@ public function __construct(TestCase $testCase, string $type)
/**
* Creates a mock object using a fluent interface.
*
* @throws ClassAlreadyExistsException
* @throws ClassIsEnumerationException
* @throws ClassIsFinalException
* @throws ClassIsReadonlyException
* @throws DuplicateMethodException
* @throws InvalidArgumentException
* @throws InvalidMethodNameException
* @throws NameAlreadyInUseException
* @throws OriginalConstructorInvocationRequiredException
* @throws ReflectionException
* @throws RuntimeException
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/Framework/MockObject/Creation/MockBuilderTest.php
Expand Up @@ -15,15 +15,15 @@
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Medium;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\MockObject\Generator\ClassAlreadyExistsException;
use PHPUnit\Framework\MockObject\Generator\NameAlreadyInUseException;
use PHPUnit\Framework\TestCase;
use PHPUnit\TestFixture\MockObject\AbstractClass;
use PHPUnit\TestFixture\MockObject\ExtendableClass;
use PHPUnit\TestFixture\MockObject\InterfaceWithReturnTypeDeclaration;
use PHPUnit\TestFixture\MockObject\TraitWithConcreteAndAbstractMethod;

#[CoversClass(MockBuilder::class)]
#[CoversClass(ClassAlreadyExistsException::class)]
#[CoversClass(NameAlreadyInUseException::class)]
#[CoversClass(CannotUseAddMethodsException::class)]
#[Group('test-doubles')]
#[Group('test-doubles/creation')]
Expand All @@ -46,7 +46,7 @@ public function testCanCreateMockObjectWithSpecifiedClassName(): void
#[TestDox('setMockClassName() cannot be used to configure the name of the mock object class when a class with that name already exists')]
public function testCannotCreateMockObjectWithSpecifiedClassNameWhenClassWithThatNameAlreadyExists(): void
{
$this->expectException(ClassAlreadyExistsException::class);
$this->expectException(NameAlreadyInUseException::class);

$this->getMockBuilder(InterfaceWithReturnTypeDeclaration::class)
->setMockClassName(__CLASS__)
Expand Down

0 comments on commit 63183f2

Please sign in to comment.