{"record":{"id":"be3c87fc020be920","repo":"sebastianbergmann/phpunit","slug":"test-directory-s-not-found","errorCode":null,"errorMessage":"Test directory \"%s\" not found","messagePattern":"Test directory \"(.+?)\" not found","errorType":"exception","errorClass":"PHPUnit\\TextUI\\TestDirectoryNotFoundException","httpStatus":null,"severity":"error","filePath":"src/TextUI/Configuration/Xml/TestSuiteMapper.php","lineNumber":86,"sourceCode":"                }\n\n                if ($excludeTestSuites !== [] && in_array($configuredTestSuite->name(), $excludeTestSuites, true)) {\n                    continue;\n                }\n\n                $testSuiteName = $configuredTestSuite->name();\n                $exclude       = [];\n\n                foreach ($configuredTestSuite->exclude()->asArray() as $file) {\n                    $exclude[] = $file->path();\n                }\n\n                $testSuite = TestSuiteObject::empty($configuredTestSuite->name());\n                $empty     = true;\n\n                foreach ($configuredTestSuite->directories() as $directory) {\n                    if (!str_contains($directory->path(), '*') && !is_dir($directory->path())) {\n                        throw new TestDirectoryNotFoundException($directory->path());\n                    }\n\n                    if (!version_compare(PHP_VERSION, $directory->phpVersion(), $directory->phpVersionOperator()->asString())) {\n                        continue;\n                    }\n\n                    $files = (new Facade)->getFilesAsArray(\n                        $directory->path(),\n                        $directory->suffix(),\n                        $directory->prefix(),\n                        $exclude,\n                    );\n\n                    $groups = $directory->groups();\n\n                    foreach ($files as $file) {\n                        if ($this->wasAlreadyAddedToAnotherTestSuite($processed, $file, $testSuiteName)) {\n                            continue;","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/TextUI/Configuration/Xml/TestSuiteMapper.php#L68-L104","documentation":"While mapping <testsuite> entries from phpunit.xml into a runnable test suite, TestSuiteMapper::map() checks each <directory> path that contains no '*' wildcard with is_dir() and throws PHPUnit\\TextUI\\TestDirectoryNotFoundException when it is not an existing directory. Wildcard-containing paths are exempt because they are expanded by the file iterator later. Since PHPUnit 10, relative paths in the XML are resolved relative to the directory containing the configuration file.","triggerScenarios":"`<directory>tests/Unit</directory>` after tests/Unit was renamed or deleted; phpunit.xml moved into a subdirectory so its relative <directory> entries now point at the wrong location; directory name typos; directory exists only on another branch checked out in CI.","commonSituations":"Restructuring the tests tree without updating phpunit.xml; moving the config file (e.g. into build/ or .phpunit.d/) during CI experiments; monorepos where the config is reused from a different package root.","solutions":["Correct the path in phpunit.xml; remember relative <directory> paths resolve against the XML file's own directory","If the directory is optional, append a wildcard (e.g. `tests/Optional/*`) so the existence check is skipped","Recreate the directory if it should exist"],"exampleFix":"<!-- before: phpunit.xml moved into .ci/ so 'tests' no longer resolves -->\n<testsuites>\n  <testsuite name=\"default\">\n    <directory>tests</directory>\n  </testsuite>\n</testsuites>\n\n<!-- after: path relative to the XML file's location -->\n<testsuites>\n  <testsuite name=\"default\">\n    <directory>../tests</directory>\n  </testsuite>\n</testsuites>","handlingStrategy":"validation","validationCode":"// Pre-flight the <directory> entries of phpunit.xml (paths resolve relative to the XML file)\n$configDir = dirname(realpath('phpunit.xml'));\n$xml = simplexml_load_file('phpunit.xml');\n\nforeach ($xml->xpath('//testsuite/directory') as $dir) {\n    $path = (string) $dir;\n\n    if (!str_contains($path, '*') && !is_dir($configDir . DIRECTORY_SEPARATOR . $path) && !is_dir($path)) {\n        fwrite(STDERR, \"Configured test directory missing: {$path}\" . PHP_EOL);\n        exit(2);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    $suite = (new \\PHPUnit\\TextUI\\XmlConfiguration\\TestSuiteMapper)->map(...);\n} catch (\\PHPUnit\\TextUI\\TestDirectoryNotFoundException $e) {\n    // message names the missing directory; fix the <directory> entry (or add a '*' wildcard) in phpunit.xml\n}","preventionTips":["After moving/renaming test directories, update every <directory> entry in phpunit.xml","Since PHPUnit 10, relative <directory> paths resolve against the XML file's directory, not the cwd","Appending /* to optional directories skips the existence check (wildcards are expanded later)"],"tags":["phpunit","xml","test-suite","directory-not-found","configuration"],"backgroundTag":"test-directory-not-found","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}