pestphp/pest · critical · FatalException

The test directory [%s] does not exist.

Error message

The test directory [%s] does not exist.

What it means

Pest computes the test directory as TestSuite rootPath + the value returned by testDirectory() (default 'tests', overridable via the TEST_DIRECTORY env variable or Pest configuration) and verifies it exists during bootstrapping, before any test file loads. When is_dir() fails, BootFiles throws this FatalException and the entire run aborts immediately. It is a configuration/layout error, not a test failure.

Source

Thrown at src/Bootstrappers/BootFiles.php:40

{
    /**
     * @var array<int, string>
     */
    private const array STRUCTURE = [
        'Expectations',
        'Expectations.php',
        'Helpers',
        'Helpers.php',
        'Pest.php',
    ];

    public function boot(): void
    {
        $rootPath = TestSuite::getInstance()->rootPath;
        $testsPath = $rootPath.DIRECTORY_SEPARATOR.testDirectory();

        if (! is_dir($testsPath)) {
            throw new FatalException(sprintf('The test directory [%s] does not exist.', $testsPath));
        }

        foreach (self::STRUCTURE as $filename) {
            $filename = sprintf('%s%s%s', $testsPath, DIRECTORY_SEPARATOR, $filename);

            if (! file_exists($filename)) {
                continue;
            }

            if (is_dir($filename)) {
                $directory = new RecursiveDirectoryIterator($filename);
                $iterator = new RecursiveIteratorIterator($directory);
                /** @var \DirectoryIterator $file */
                foreach ($iterator as $file) {
                    $this->load($file->__toString());
                }
            } else {
                $this->load($filename);

View on GitHub (pinned to 1af74a215c)

Solutions

  1. Check which directory Pest resolves: the message contains the full path — verify it against where your tests actually live, and fix TEST_DIRECTORY in phpunit.xml/.env if it is wrong.
  2. Create the missing directory (mkdir -p tests) if the project genuinely should have one and add a bootstrap Pest.php.
  3. Run vendor/bin/pest from the project root (where composer.json and the tests directory sit) so rootPath is correct.
  4. On case-sensitive filesystems (Linux CI), confirm the directory casing matches exactly (Tests vs tests).

Example fix

<!-- phpunit.xml -->
// before
<php><env name="TEST_DIRECTORY" value="specs"/></php>  <!-- folder is actually "tests" -->
// after
<php><env name="TEST_DIRECTORY" value="tests"/></php>
Defensive patterns

Strategy: validation

Validate before calling

// before running pest (CI step or composer script):
$dir = getcwd().'/'.(getenv('TEST_DIRECTORY') ?: 'tests');
if (! is_dir($dir)) {
    fwrite(STDERR, "Test directory missing: {$dir}\n");
    exit(1);
}

Prevention

When it happens

Trigger: Running vendor/bin/pest when the configured TEST_DIRECTORY points to a folder that does not exist; renaming tests/ to specs/ (or moving it into a subdirectory) without updating phpunit.xml's <env name="TEST_DIRECTORY" .../>; running Pest from the wrong working directory so rootPath resolves to a folder without tests; a case mismatch such as Tests/ vs tests/ on a case-sensitive filesystem.

Common situations: Fresh clones where the repo keeps tests in a non-default location and the env var is not set; CI pipelines that exclude the tests directory from the checkout artifact; monorepos where vendor/bin/pest is executed from the package root instead of the app root; after a directory restructure the .env or phpunit.xml still names the old path.

Related errors


AI-assisted analysis of pestphp/pest@1af74a215c (2026-08-21). Data as JSON: /api/errors/4f70793eeec13d14. Report an issue: GitHub.