sebastianbergmann/phpunit · error · UnknownTypeException
Class or interface "%s" does not exist
Error message
Class or interface "%s" does not exist
What it means
Before building a test double for a type name, PHPUnit's Generator::ensureKnownType() checks that the string denotes an existing class or interface; otherwise it throws UnknownTypeException. This is the first gate inside testDouble() and catches bad type names before reflection or code generation is attempted.
Source
Thrown at src/Framework/MockObject/Generator/Generator.php:697
'__halt_compiler' => true,
];
if (version_compare(PHP_VERSION, '8.5', '>=')) {
self::$excludedMethodNames['__sleep'] = true;
self::$excludedMethodNames['__wakeup'] = true;
}
}
return isset(self::$excludedMethodNames[$name]);
}
/**
* @throws UnknownTypeException
*/
private function ensureKnownType(string $type): void
{
if (!class_exists($type) && !interface_exists($type)) {
throw new UnknownTypeException($type);
}
}
/**
* @param ?list<non-empty-string> $methods
*
* @throws DuplicateMethodException
* @throws InvalidMethodNameException
*/
private function ensureValidMethods(?array $methods): void
{
if ($methods === null) {
return;
}
foreach ($methods as $method) {
if (preg_match('~\A[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\z~', (string) $method) === 0) {
throw new InvalidMethodNameException((string) $method);View on GitHub (pinned to f123cdb2a2)
Solutions
- Correct the type name; prefer SomeClass::class over string literals so the autoloader resolves it at compile time
- Run composer dump-autoload and make sure the file/package defining the type is installed and autoloadable in the test environment
Example fix
// before
$mock = $this->createMock('App\Service\Mailler'); // typo
// after
use App\Service\Mailer;
$mock = $this->createMock(Mailer::class); Defensive patterns
Strategy: validation
Validate before calling
if (!class_exists($type) && !interface_exists($type)) {
$this->markTestSkipped("Cannot double unknown type: $type");
}
$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
- Pass Type::class constants, never hand-built strings, to createMock()
- Run composer dump-autoload after adding or moving classes; verify CI installs the package owning the type
When it happens
Trigger: createMock('App\Servces\Mailer') (typo), createMock($typeName) with a dynamic name that is null/empty/mangled, createMock(SomeClass::class) where SomeClass is not imported or its package is not autoloaded in the test runtime, or names built by string concatenation with wrong separators (leading backslash is fine, wrong case or missing namespace is not).
Common situations: Data-driven tests generating mock types from configuration; refactors renaming classes while the string literal in tests lags behind; composer classmap stale after new files added; running an isolated test file whose dependencies were never autoloaded; CI missing a dev package.
Related errors
- Class or interface "%s" does not exist
- Class "%s" is an anonymous class and cannot be doubled
- Class "%s" is an enumeration and cannot be doubled
- Class "%s" is declared "final" and cannot be doubled
- Event class "%s" does not exist
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/e06c7591661e9e54.
Report an issue: GitHub.