sebastianbergmann/phpunit · error · ClassDoesNotExtendTestCaseException
Class %s declared in %s does not extend PHPUnit\Framework\Te
Error message
Class %s declared in %s does not extend PHPUnit\Framework\TestCase
What it means
TestSuiteLoader::load() derives the expected class name from the filename (basename minus .php, cut at the first dot) and, in a fallback loop, looks at all classes loaded from the file: when a loaded class name ends with that expected name (case-insensitive) but no matching class extended PHPUnit\Framework\TestCase (the first loop already skipped non-TestCase and anonymous classes), it throws ClassDoesNotExtendTestCaseException('Class %s declared in %s does not extend PHPUnit\Framework\TestCase').
Source
Thrown at src/Runner/TestSuiteLoader.php:91
if (!str_ends_with(strtolower($class->getShortName()), strtolower($suiteClassName))) {
continue;
}
if (!$class->isAbstract()) {
return $class;
}
$e = new ClassIsAbstractException($class->getName(), $suiteClassFile);
}
if (isset($e)) {
throw $e;
}
foreach ($loadedClasses as $className) {
if (str_ends_with(strtolower($className), strtolower($suiteClassName))) {
throw new ClassDoesNotExtendTestCaseException($className, $suiteClassFile);
}
}
throw new ClassCannotBeFoundException($suiteClassName, $suiteClassFile);
}
private function classNameFromFileName(string $suiteClassFile): string
{
$className = basename($suiteClassFile, '.php');
$dotPos = strpos($className, '.');
if ($dotPos !== false) {
$className = substr($className, 0, $dotPos);
}
return $className;
}
View on GitHub (pinned to f123cdb2a2)
Solutions
- Make the class extend PHPUnit\Framework\TestCase: class ExampleTest extends \PHPUnit\Framework\TestCase.
- If the class is a helper, move it out of the file you pass to phpunit or rename the file so phpunit does not treat it as the test class.
- Check intermediate base classes: the whole chain must bottom out at PHPUnit\Framework\TestCase (isSubclassOf is used, so traits do not count).
Example fix
// before
class ExampleTest
{
public function testItWorks(): void
{
assert(true);
}
}
// after
class ExampleTest extends \PHPUnit\Framework\TestCase
{
public function testItWorks(): void
{
self::assertTrue(true);
}
} Defensive patterns
Strategy: type-guard
Validate before calling
$file = 'tests/ExampleTest.php';
require_once $file;
$candidates = array_filter(get_declared_classes(), fn (string $c) => str_ends_with(strtolower($c), 'exampletest'));
foreach ($candidates as $c) {
if (!(new ReflectionClass($c))->isSubclassOf(\PHPUnit\Framework\TestCase::class)) {
throw new RuntimeException("{$c} does not extend PHPUnit\\Framework\\TestCase");
}
} Type guard
/** @phpstan-assert class-string<\PHPUnit\Framework\TestCase> $className */
function isTestCaseClass(string $className): bool
{
return is_subclass_of($className, \PHPUnit\Framework\TestCase::class);
} Prevention
- Enforce that every *Test.php file's class extends PHPUnit\Framework\TestCase via a CI architecture test or static analysis rule.
- Keep helpers and base classes that are not tests out of *Test.php filenames.
When it happens
Trigger: Running 'phpunit src/ExampleTest.php' where ExampleTest exists in the file but is a plain class, an interface/trait consumer, extends a different base (e.g. TestCase of another framework), or extends PHPUnit Framework TestCase only indirectly via a broken parent chain.
Common situations: Renaming a class from a fixture to a test without adding the extends clause; mixing test frameworks (Codeception, Behat, custom base classes) and pointing phpunit at their files; extending a base TestCase class that was refactored away from PHPUnit\Framework\TestCase.
Related errors
- Class %s cannot be found in %s
- Subscriber "%s" does not implement any known interface - did
- Unknown event type "%s"
- Subscriber "%s" does not implement a known interface
- Subscriber "%s" does not exist or is not an interface
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/a525e8e8f745f745.
Report an issue: GitHub.