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

  1. Extract the anonymous class into a named (possibly internal/test-only) class and mock that name
  2. Mock the interface/base class the anonymous class implements instead of the anonymous type itself
  3. 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

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


AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23). Data as JSON: /api/errors/d94fded42ed03ef0. Report an issue: GitHub.