sebastianbergmann/phpunit · error · PHPUnit\TextUI\TestDirectoryNotFoundException
Test directory "%s" not found
Error message
Test directory "%s" not found
What it means
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.
Source
Thrown at src/TextUI/Configuration/Xml/TestSuiteMapper.php:86
}
if ($excludeTestSuites !== [] && in_array($configuredTestSuite->name(), $excludeTestSuites, true)) {
continue;
}
$testSuiteName = $configuredTestSuite->name();
$exclude = [];
foreach ($configuredTestSuite->exclude()->asArray() as $file) {
$exclude[] = $file->path();
}
$testSuite = TestSuiteObject::empty($configuredTestSuite->name());
$empty = true;
foreach ($configuredTestSuite->directories() as $directory) {
if (!str_contains($directory->path(), '*') && !is_dir($directory->path())) {
throw new TestDirectoryNotFoundException($directory->path());
}
if (!version_compare(PHP_VERSION, $directory->phpVersion(), $directory->phpVersionOperator()->asString())) {
continue;
}
$files = (new Facade)->getFilesAsArray(
$directory->path(),
$directory->suffix(),
$directory->prefix(),
$exclude,
);
$groups = $directory->groups();
foreach ($files as $file) {
if ($this->wasAlreadyAddedToAnotherTestSuite($processed, $file, $testSuiteName)) {
continue;View on GitHub (pinned to f123cdb2a2)
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
Example fix
<!-- before: phpunit.xml moved into .ci/ so 'tests' no longer resolves -->
<testsuites>
<testsuite name="default">
<directory>tests</directory>
</testsuite>
</testsuites>
<!-- after: path relative to the XML file's location -->
<testsuites>
<testsuite name="default">
<directory>../tests</directory>
</testsuite>
</testsuites> Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight the <directory> entries of phpunit.xml (paths resolve relative to the XML file)
$configDir = dirname(realpath('phpunit.xml'));
$xml = simplexml_load_file('phpunit.xml');
foreach ($xml->xpath('//testsuite/directory') as $dir) {
$path = (string) $dir;
if (!str_contains($path, '*') && !is_dir($configDir . DIRECTORY_SEPARATOR . $path) && !is_dir($path)) {
fwrite(STDERR, "Configured test directory missing: {$path}" . PHP_EOL);
exit(2);
}
} Try / catch
try {
$suite = (new \PHPUnit\TextUI\XmlConfiguration\TestSuiteMapper)->map(...);
} catch (\PHPUnit\TextUI\TestDirectoryNotFoundException $e) {
// message names the missing directory; fix the <directory> entry (or add a '*' wildcard) in phpunit.xml
} Prevention
- 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)
When it happens
Trigger: `<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.
Common situations: 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.
Related errors
- Default test suite is not configured
- Test file "%s" not found
- Class %s is abstract
- Parameter "%s" does not exist
- Cannot load XML configuration file %s
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/be3c87fc020be920.
Report an issue: GitHub.