sebastianbergmann/phpunit · error · ClassIsAnonymousException
Class "%s" is an anonymous class and cannot be doubled
Error message
Class "%s" is an anonymous class and cannot be doubled
What it means
Before generating a test double, PHPUnit reflects the target class and rejects anonymous classes (ReflectionClass::isAnonymous()). Mocks are built by generating a subclass named after the original; an anonymous class has no stable name that can serve as a parent, so it cannot be doubled.
Source
Thrown at src/Framework/MockObject/Generator/Generator.php:386
$mockMethods = new DoubledMethodSet;
$testDoubleClassPrefix = $mockObject ? 'MockObject_' : 'TestStub_';
$_mockClassName = $this->generateClassName(
$type,
$mockClassName,
$testDoubleClassPrefix,
);
if (class_exists($_mockClassName['fullClassName'])) {
$isClass = true;
} elseif (interface_exists($_mockClassName['fullClassName'])) {
$isInterface = true;
}
$class = $this->reflectClass($_mockClassName['fullClassName']);
if ($class->isAnonymous()) {
throw new ClassIsAnonymousException($_mockClassName['fullClassName']);
}
if ($class->isEnum()) {
throw new ClassIsEnumerationException($_mockClassName['fullClassName']);
}
if ($class->isFinal()) {
throw new ClassIsFinalException($_mockClassName['fullClassName']);
}
if ($class->isReadOnly()) {
$isReadonly = true;
}
// @see https://github.com/sebastianbergmann/phpunit/issues/2995
if ($isInterface && $class->implementsInterface(Throwable::class)) {
$actualClassName = Exception::class;
$additionalInterfaces[] = $class->getName();View on GitHub (pinned to f123cdb2a2)
Solutions
- Extract the anonymous class into a named (possibly internal/test-only) class and mock that name
- Mock the interface/base class the anonymous class implements instead of the anonymous type itself
- For simple stand-ins, build the anonymous object by hand with the behavior you need instead of mocking it
Example fix
// before
$handler = new class implements HandlerInterface { ... };
$mock = $this->createMock(get_class($handler)); // anonymous -> exception
// after
$mock = $this->createMock(HandlerInterface::class); Defensive patterns
Strategy: validation
Validate before calling
$type = $object::class;
if ((new ReflectionClass($type))->isAnonymous()) {
$type = class_implements($object)[0] ?? get_parent_class($object);
}
$mock = $this->createMock($type); Type guard
function isMockableType(string $type): bool
{
if (!class_exists($type) && !interface_exists($type)) {
return false;
}
$r = new ReflectionClass($type);
return !$r->isAnonymous() && !$r->isEnum() && !$r->isFinal();
} Prevention
- Never derive a mock target from get_class() of an object that might be anonymous — type against interfaces
- Give fixtures named test-double classes instead of inline `new class`
When it happens
Trigger: createMock() (or createStub, or MockBuilder) given an anonymous class: e.g. $anon = new class {}; createMock(get_class($anon)) — get_class() returns something like 'app\...\class@anonymous...' which reflects as anonymous and triggers the exception.
Common situations: Tests for factories that return throwaway anonymous implementations; capturing an object from SUT and trying to mock its dynamic type; using new class extends SomeBase inline in fixtures then attempting to double it.
Related errors
- Class "%s" is an enumeration and cannot be doubled
- Class "%s" is declared "final" and cannot be doubled
- Class or interface "%s" does not exist
- At least two interfaces must be specified
- Interface "%s" does not exist
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/d94fded42ed03ef0.
Report an issue: GitHub.