sebastianbergmann/phpunit · error · Exception
Class %s is not a subclass of %s
Error message
Class %s is not a subclass of %s
What it means
Thrown by TestSuite::addTestSuite() when the given class is concrete but does not extend PHPUnit\Framework\TestCase (checked with is_subclass_of()). TestSuite instances aggregate actual tests, so any class registered as a test suite must be a TestCase subclass.
Source
Thrown at src/Framework/TestSuite.php:220
* @param positive-int $maxAttempts
*
* @throws Exception
*/
public function addTestSuite(ReflectionClass $testClass, array $groups = [], int $numberOfRuns = 1, int $maxAttempts = 1): void
{
$className = $testClass->getName();
if ($testClass->isAbstract()) {
throw new Exception(
sprintf(
'Class %s is abstract',
$className,
),
);
}
if (!is_subclass_of($className, TestCase::class)) {
throw new Exception(
sprintf(
'Class %s is not a subclass of %s',
$className,
TestCase::class,
),
);
}
$this->addTest(self::fromClassReflector($testClass, $groups, $numberOfRuns, $maxAttempts), $groups);
}
/**
* Wraps both <code>addTest()</code> and <code>addTestSuite</code>
* as well as the separate import statements for the user's convenience.
*
* If the named file cannot be read or there are no new tests that can be
* added, a <code>PHPUnit\Framework\WarningTestCase</code> will be created instead,
* leaving the current test run untouched.View on GitHub (pinned to f123cdb2a2)
Solutions
- Pass only classes that extend PHPUnit\Framework\TestCase to addTestSuite()
- Rename support classes in the tests directory so they do not match the test suffix/prefix your loader scans (e.g. 'Helper' instead of 'Test')
- Exclude the directory or file containing non-test classes in phpunit.xml
Example fix
// before $suite->addTestSuite(new ReflectionClass(TestDataFactory::class)); // not a TestCase // after $suite->addTestSuite(new ReflectionClass(UserRepositoryTest::class));
Defensive patterns
Strategy: type-guard
Validate before calling
if (!is_subclass_of($className, \PHPUnit\Framework\TestCase::class)) {
// not a test class: do not register it, log it for cleanup instead
} Type guard
function isRunnableTestClass(ReflectionClass $class): bool
{
return !$class->isAbstract()
&& $class->isSubclassOf(\PHPUnit\Framework\TestCase::class);
} Prevention
- Name support classes in tests/ without the Test suffix/prefix your loader matches
- Validate dynamically discovered classes with is_subclass_of(..., TestCase::class) before registration
When it happens
Trigger: Calling addTestSuite(new ReflectionClass($class)) where $class is a plain helper, fixture, or data class; loading a tests directory that contains non-test classes (helpers, traits consumers, DTOs) whose files match the test file naming convention.
Common situations: Helper classes or support code stored in the tests directory with names like *Test.php or *TestCase.php; typos when referencing a test class name; custom runners registering arbitrary classes.
Related errors
- Class %s is abstract
- The tests aggregated by this TestSuite were already run
- The current item is not a TestSuite instance and therefore d
- Default test suite is not configured
- Test directory "%s" not found
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/3bc333bb100f1f64.
Report an issue: GitHub.