{"record":{"id":"b636887a0d91701d","repo":"sebastianbergmann/phpunit","slug":"class-s-is-abstract","errorCode":null,"errorMessage":"Class %s is abstract","messagePattern":"Class (.+?) is abstract","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/Framework/TestSuite.php","lineNumber":211,"sourceCode":"        }\n    }\n\n    /**\n     * Adds the tests from the given class to the suite.\n     *\n     * @param ReflectionClass<TestCase> $testClass\n     * @param list<non-empty-string>    $groups\n     * @param positive-int              $numberOfRuns\n     * @param positive-int              $maxAttempts\n     *\n     * @throws Exception\n     */\n    public function addTestSuite(ReflectionClass $testClass, array $groups = [], int $numberOfRuns = 1, int $maxAttempts = 1): void\n    {\n        $className = $testClass->getName();\n\n        if ($testClass->isAbstract()) {\n            throw new Exception(\n                sprintf(\n                    'Class %s is abstract',\n                    $className,\n                ),\n            );\n        }\n\n        if (!is_subclass_of($className, TestCase::class)) {\n            throw new Exception(\n                sprintf(\n                    'Class %s is not a subclass of %s',\n                    $className,\n                    TestCase::class,\n                ),\n            );\n        }\n\n        $this->addTest(self::fromClassReflector($testClass, $groups, $numberOfRuns, $maxAttempts), $groups);","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Framework/TestSuite.php#L193-L229","documentation":"Thrown by TestSuite::addTestSuite() when the ReflectionClass passed refers to an abstract class. An abstract class cannot be instantiated or run as a test suite, so PHPUnit rejects it explicitly before trying to collect its test methods.","triggerScenarios":"Calling $suite->addTestSuite(new ReflectionClass($someAbstractClass)) directly, or pointing the CLI/loader at a file whose class is abstract and extends TestCase (common for base test classes holding shared fixtures).","commonSituations":"An abstract TestCase base class (e.g. AbstractApiTestCase) living in a directory the test loader scans; passing a base class name to addTestSuite() by mistake.","solutions":["Point addTestSuite() at the concrete subclasses instead of the abstract base","Add the abstract base class file to the exclusion list of your testsuite directory configuration in phpunit.xml","Rename/move abstract test case classes outside the scanned tests directory (e.g. to src/ or a supports/ folder)"],"exampleFix":"// before\n$suite->addTestSuite(new ReflectionClass(AbstractApiTestCase::class)); // abstract base\n\n// after\n$suite->addTestSuite(new ReflectionClass(UserApiTest::class)); // concrete child","handlingStrategy":"type-guard","validationCode":"$class = new ReflectionClass($candidate);\nif ($class->isAbstract()) {\n    // register concrete children: $class->isAbstract() ? skip : addTestSuite($class)\n}","typeGuard":"function isRunnableTestClass(ReflectionClass $class): bool\n{\n    return !$class->isAbstract()\n        && $class->isSubclassOf(\\PHPUnit\\Framework\\TestCase::class);\n}","tryCatchPattern":null,"preventionTips":["Filter candidate classes through isRunnableTestClass() before addTestSuite() in custom loaders","Keep abstract base test classes out of scanned testsuite directories (suffix/prefix conventions)"],"tags":["phpunit","test-suite","abstract-class","configuration"],"backgroundTag":"abstract-class-cannot-run","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}