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

  1. Correct the path in phpunit.xml; remember relative <directory> paths resolve against the XML file's own directory
  2. If the directory is optional, append a wildcard (e.g. `tests/Optional/*`) so the existence check is skipped
  3. 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

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


AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23). Data as JSON: /api/errors/be3c87fc020be920. Report an issue: GitHub.