{"record":{"id":"a525e8e8f745f745","repo":"sebastianbergmann/phpunit","slug":"class-s-declared-in-s-does-not-extend-phpunit-fr","errorCode":null,"errorMessage":"Class %s declared in %s does not extend PHPUnit\\Framework\\TestCase","messagePattern":"Class (.+?) declared in (.+?) does not extend PHPUnit\\\\Framework\\\\TestCase","errorType":"exception","errorClass":"ClassDoesNotExtendTestCaseException","httpStatus":null,"severity":"error","filePath":"src/Runner/TestSuiteLoader.php","lineNumber":91,"sourceCode":"\n            if (!str_ends_with(strtolower($class->getShortName()), strtolower($suiteClassName))) {\n                continue;\n            }\n\n            if (!$class->isAbstract()) {\n                return $class;\n            }\n\n            $e = new ClassIsAbstractException($class->getName(), $suiteClassFile);\n        }\n\n        if (isset($e)) {\n            throw $e;\n        }\n\n        foreach ($loadedClasses as $className) {\n            if (str_ends_with(strtolower($className), strtolower($suiteClassName))) {\n                throw new ClassDoesNotExtendTestCaseException($className, $suiteClassFile);\n            }\n        }\n\n        throw new ClassCannotBeFoundException($suiteClassName, $suiteClassFile);\n    }\n\n    private function classNameFromFileName(string $suiteClassFile): string\n    {\n        $className = basename($suiteClassFile, '.php');\n        $dotPos    = strpos($className, '.');\n\n        if ($dotPos !== false) {\n            $className = substr($className, 0, $dotPos);\n        }\n\n        return $className;\n    }\n","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Runner/TestSuiteLoader.php#L73-L109","documentation":"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').","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nclass ExampleTest\n{\n    public function testItWorks(): void\n    {\n        assert(true);\n    }\n}\n\n// after\nclass ExampleTest extends \\PHPUnit\\Framework\\TestCase\n{\n    public function testItWorks(): void\n    {\n        self::assertTrue(true);\n    }\n}","handlingStrategy":"type-guard","validationCode":"$file = 'tests/ExampleTest.php';\nrequire_once $file;\n\n$candidates = array_filter(get_declared_classes(), fn (string $c) => str_ends_with(strtolower($c), 'exampletest'));\n\nforeach ($candidates as $c) {\n    if (!(new ReflectionClass($c))->isSubclassOf(\\PHPUnit\\Framework\\TestCase::class)) {\n        throw new RuntimeException(\"{$c} does not extend PHPUnit\\\\Framework\\\\TestCase\");\n    }\n}","typeGuard":"/** @phpstan-assert class-string<\\PHPUnit\\Framework\\TestCase> $className */\nfunction isTestCaseClass(string $className): bool\n{\n    return is_subclass_of($className, \\PHPUnit\\Framework\\TestCase::class);\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["phpunit","test-loader","base-class","wrong-inheritance"],"backgroundTag":"wrong-base-class","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}