sebastianbergmann/phpunit · error · PHPUnit\TextUI\TestFileNotFoundException

Test file "%s" not found

Error message

Test file "%s" not found

What it means

The <file> counterpart of the directory check: TestSuiteMapper::map() runs is_file() on every <file> entry of a <testsuite> and throws PHPUnit\TextUI\TestFileNotFoundException with the configured path when it does not exist. Unlike <directory> entries, there is no wildcard exemption.

Source

Thrown at src/TextUI/Configuration/Xml/TestSuiteMapper.php:132

                        $empty            = false;

                        if ($this->skipper->canSkipLoading($file, $groups)) {
                            continue;
                        }

                        $this->skipper->record(
                            $file,
                            static function () use ($testSuite, $file, $groups, $numberOfRuns, $maxAttempts): void
                            {
                                $testSuite->addTestFile($file, $groups, $numberOfRuns, $maxAttempts);
                            },
                        );
                    }
                }

                foreach ($configuredTestSuite->files() as $file) {
                    if (!is_file($file->path())) {
                        throw new TestFileNotFoundException($file->path());
                    }

                    if (!version_compare(PHP_VERSION, $file->phpVersion(), $file->phpVersionOperator()->asString())) {
                        continue;
                    }

                    if ($this->wasAlreadyAddedToAnotherTestSuite($processed, $file->path(), $testSuiteName)) {
                        continue;
                    }

                    $processed[$file->path()] = $testSuiteName;
                    $empty                    = false;

                    if ($this->skipper->canSkipLoading($file->path(), $file->groups())) {
                        continue;
                    }

                    $this->skipper->record(

View on GitHub (pinned to f123cdb2a2)

Solutions

  1. Update or remove the stale <file> entry in phpunit.xml
  2. Fix casing so the XML matches the filesystem byte-for-byte (critical for Linux CI)
  3. Prefer <directory> with suffix filters over long <file> lists when files churn

Example fix

<!-- before -->
<testsuite name="smoke">
  <file>tests/smoketest.php</file>
</testsuite>

<!-- after (actual name is SmokeTest.php) -->
<testsuite name="smoke">
  <file>tests/SmokeTest.php</file>
</testsuite>
Defensive patterns

Strategy: validation

Validate before calling

$configDir = dirname(realpath('phpunit.xml'));
$xml = simplexml_load_file('phpunit.xml');

foreach ($xml->xpath('//testsuite/file') as $file) {
    $path = (string) $file;

    if (!is_file($configDir . DIRECTORY_SEPARATOR . $path) && !is_file($path)) {
        fwrite(STDERR, "Configured test file missing: {$path}" . PHP_EOL);
        exit(2);
    }
}

Try / catch

try {
    $suite = (new \PHPUnit\TextUI\XmlConfiguration\TestSuiteMapper)->map(...);
} catch (\PHPUnit\TextUI\TestFileNotFoundException $e) {
    // message names the missing <file> path; remove or correct the entry (watch filename casing on Linux)
}

Prevention

When it happens

Trigger: `<file>tests/SmokeTest.php</file>` where the file was deleted or renamed; case mismatches between the XML and the filesystem (developing case-insensitively on macOS/Windows, CI running case-sensitively on Linux); a single-file suite entry left behind after a split-up.

Common situations: Renamed or deleted test files still listed in phpunit.xml; case-sensitivity differences between developer machines and Linux CI; files moved during refactors.

Related errors


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