sebastianbergmann/phpunit · error · UnknownClassOrInterfaceException
Class or interface "%s" does not exist
Error message
Class or interface "%s" does not exist
What it means
Assert::assertInstanceOf() validates its first argument before evaluating the constraint: if the string is neither an existing nor autoloadable class or interface, it throws PHPUnit\Framework\UnknownClassOrInterfaceException ('Class or interface "%s" does not exist') instead of running the assertion. This is a test-code error (malformed assertion), not an assertion failure — the $actual value is irrelevant because the check fails first.
Source
Thrown at src/Framework/Assert.php:1870
}
/**
* Asserts that a variable is of a given type.
*
* @template ExpectedType of object
*
* @param class-string<ExpectedType> $expected
*
* @throws Exception
* @throws ExpectationFailedException
* @throws UnknownClassOrInterfaceException
*
* @phpstan-assert =ExpectedType $actual
*/
final public static function assertInstanceOf(string $expected, mixed $actual, string $message = ''): void
{
if (!class_exists($expected) && !interface_exists($expected)) {
throw new UnknownClassOrInterfaceException($expected);
}
self::assertThat(
$actual,
new IsInstanceOf($expected),
$message,
);
}
/**
* Asserts that a variable is not of a given type.
*
* @template ExpectedType of object
*
* @param class-string<ExpectedType> $expected
*
* @throws Exception
* @throws ExpectationFailedExceptionView on GitHub (pinned to f123cdb2a2)
Solutions
- Replace string literals with the ::class constant and a use import: self::assertInstanceOf(UserRepo::class, $user).
- Fix the FQCN spelling/namespace and run class_exists('The\Fqcn') in 'php -r' to confirm the autoloader resolves it.
- Run 'composer dump-autoload' (or a full 'composer install') if the class exists on disk but is not autoloaded.
- For dynamic class names, validate the string before the assertion with class_exists()/interface_exists() and fail with a clear message.
Example fix
// before
self::assertInstanceOf('App\Servic\UserRepository', $user);
// after
use App\Service\UserRepository;
self::assertInstanceOf(UserRepository::class, $user); Defensive patterns
Strategy: type-guard
Validate before calling
if (!class_exists($expected) && !interface_exists($expected)) {
self::fail('Unknown class or interface: ' . $expected . ' (fix the FQCN or autoload mapping)');
}
self::assertInstanceOf($expected, $actual); Type guard
/** @phpstan-assert class-string $name */
function assertValidTypeString(string $name): void
{
if (!class_exists($name) && !interface_exists($name)) {
throw new InvalidArgumentException("Class or interface '{$name}' does not exist");
}
} Prevention
- Use ClassName::class (with a use import) instead of quoted FQCN strings in assertions.
- Keep dev dependencies installed in CI so referenced contract classes always exist.
- Validate data-provider-supplied class strings before passing them to assertions.
When it happens
Trigger: self::assertInstanceOf('App\Servic\UserRepo', $user) with a typo; a class string built from a variable, config, or data provider that is empty, stale, or differently cased; quoting the ::class constant so it stays a literal ('User::class'); referencing a class from a package missing from the current vendor tree.
Common situations: Refactors that rename classes while tests keep old string names; 'composer install --no-dev' CI runs missing dev-only packages; case-sensitive class names on Linux after a rename; leading-backslash inconsistencies in FQCNs built by concatenation.
Related errors
- Event class "%s" does not exist
- Subscriber "%s" does not exist or is not an interface
- Class or interface "%s" does not exist
- Class or interface "%s" does not exist
- Subscriber "%s" does not implement any known interface - did
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/5604568a439b2a68.
Report an issue: GitHub.