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
- Update or remove the stale <file> entry in phpunit.xml
- Fix casing so the XML matches the filesystem byte-for-byte (critical for Linux CI)
- 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
- Remove or update <file> entries when the referenced tests are renamed or deleted
- Match filename casing exactly; case-insensitive local filesystems hide mismatches until Linux CI
- Prefer <directory suffix="..."> over long <file> lists when files change often
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
- Default test suite is not configured
- Test directory "%s" not found
- Class %s is abstract
- Parameter "%s" does not exist
- Cannot open bootstrap script "%s"
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/eb6331843674318f.
Report an issue: GitHub.