When running tests in parallel (--parallel), the merged TestResult can report hasTests() === false even though hundreds of tests ran and passed. With PHPUnit 12.5.23+ (which defaults failOnEmptyTestSuite on), this makes the process exit 1 while the summary still shows e.g. 640 passed.
Environment
- pest 4.7.4
- phpunit/phpunit 12.5.30
- brianium/paratest 7.20.0
- PHP 8.5
Root cause
In src/Plugins/Parallel/Paratest/WrapperRunner.php:327, worker results are merged with:
(int) $testResultSum->hasTests() + (int) $testResult->hasTests(),
This passes a boolean sum as the TestResult $numberOfTests constructor argument instead of summing the real per-worker test counts. The per-worker numberOfTests is itself unreliable in the streaming worker (it does not reflect the tests actually run — numberOfTestsRun does). So the merged "has tests" flag becomes true only if at least one worker happens to report hasTests() === true.
Observed worker data (single shard, all tests passing)
2 workers:
worker_01 hasTests=false numberOfTestsRun=401
worker_02 hasTests=false numberOfTestsRun=239
18 workers (same suite):
worker_05 hasTests=true numberOfTestsRun=12
worker_07 hasTests=true numberOfTestsRun=40
... (13 others hasTests=false, e.g. numberOfTestsRun=401/239/43)
So the failure is process-count dependent: with few workers, all of them can report hasTests=false, making the merged result look empty; with many workers, at least one flukes to true and it passes. A 2-core CI runner deterministically fails; a many-core dev machine passes.
Steps to reproduce
- A test suite large enough to span multiple parallel workers, all passing.
- Constrain to 2 CPU cores (e.g. a 2-core CI runner, or shim the CPU finder).
vendor/bin/pest --parallel → prints N passed, but exits 1.
Expected vs actual
- Expected: exit
0 when all tests pass and tests were actually run.
- Actual: exit
1, treated as an empty test suite.
Suggested fix
Sum the real test counts in the merge rather than the boolean flags, e.g.:
ResultReflection::numberOfTests($testResultSum) + ResultReflection::numberOfTests($testResult),
Workaround
Set failOnEmptyTestSuite="false" in phpunit.xml.
When running tests in parallel (
--parallel), the mergedTestResultcan reporthasTests() === falseeven though hundreds of tests ran and passed. With PHPUnit 12.5.23+ (which defaultsfailOnEmptyTestSuiteon), this makes the process exit1while the summary still shows e.g.640 passed.Environment
Root cause
In
src/Plugins/Parallel/Paratest/WrapperRunner.php:327, worker results are merged with:This passes a boolean sum as the
TestResult$numberOfTestsconstructor argument instead of summing the real per-worker test counts. The per-workernumberOfTestsis itself unreliable in the streaming worker (it does not reflect the tests actually run —numberOfTestsRundoes). So the merged "has tests" flag becomes true only if at least one worker happens to reporthasTests() === true.Observed worker data (single shard, all tests passing)
2 workers:
18 workers (same suite):
So the failure is process-count dependent: with few workers, all of them can report
hasTests=false, making the merged result look empty; with many workers, at least one flukes totrueand it passes. A 2-core CI runner deterministically fails; a many-core dev machine passes.Steps to reproduce
vendor/bin/pest --parallel→ printsN passed, but exits1.Expected vs actual
0when all tests pass and tests were actually run.1, treated as an empty test suite.Suggested fix
Sum the real test counts in the merge rather than the boolean flags, e.g.:
Workaround
Set
failOnEmptyTestSuite="false"inphpunit.xml.