itsgoingd / itsgoingd/clockwork
Test collection silently fails on PHPUnit 12 / Pest 4 — $app is already null when Passed/Failed/Skipped events fire
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 6k
- Forks
- 333
- Avg merge
- 5m
- Merged PRs (30d)
- 1
Description
Thank you for all of the work you have done on Clockwork. I recently ran into an issue getting data while running pest tests. Below is a summary from Claude as to why I am not seeing anything.
============
Environment
- itsgoingd/clockwork v5.3.5
- phpunit/phpunit 12.5.33
- pestphp/pest v4
- laravel/framework v12
- PHP 8.3.32
Config
- config/clockwork.php: tests.collect → true (via CLOCKWORK_TESTS_COLLECT=true)
- CLOCKWORK_ENABLE=true
- phpunit.xml has the extension registered correctly:
Symptom
No test metadata is ever written to storage/clockwork when running tests via php artisan test, vendor/bin/pest, or vendor/bin/phpunit, despite tests.collect being enabled and no errors being reported.
Root cause (confirmed via temporary debug logging in ClockworkExtension.php)
- ClockworkExtension::bootstrap() runs, and subscribers are registered correctly.
- PassedSubscriber/FailedSubscriber/etc. do fire recordTest() for every test.
- Inside recordTest(), resolveApp($testCase) returns null on every single test, so the method returns early before ever calling isCollectingTests() or storeRequest().
- resolveApp() (in Clockwork/Support/Laravel/Tests/ClockworkExtension.php) reflects on the test case's $app property:
protected static function resolveApp($testCase)
{
$reflectionClass = new \ReflectionClass($testCase);
if ($reflectionClass->hasProperty('app')) {
$reflectionProperty = $reflectionClass->getProperty('app');
$reflectionProperty->setAccessible(true);
if ($reflectionProperty->getValue($testCase)) {
return $reflectionProperty->getValue($testCase);
}
} elseif (method_exists($testCase, 'createApplication')) {
return $testCase->createApplication();
}
} - Laravel's Illuminate\Foundation\Testing\Concerns\InteractsWithTestCaseLifecycle::tearDownTheTestEnvironment() sets $this->app = null inside tearDown().
- In PHPUnit 12's event system, tearDown() runs before the Test\Passed (and Failed/Skipped/Errored) event is emitted (see vendor/phpunit/phpunit/src/Framework/TestCase.php around lines 621–659 — fixture teardown happens, then $emitter->testPassed(...) is dispatched).
- So by the time Clockwork's subscriber runs resolveApp(), $app has already been nulled by Laravel's own teardown, and the property check at line 102 (if ($reflectionProperty->getValue($testCase))) fails, causing a silent no-op with no error or log output.
Why this looks new: the CHANGELOG for 5.3.5 lists support for "PHPUnit 11 and Pest 3." This suggests the event-timing relative to tearDown() changed (or was already like this but tolerated differently) by PHPUnit 12, breaking the $app-via-reflection approach for PHPUnit 12 / Pest 4.
Suggested fix direction
- Capture the app instance earlier in the test lifecycle (e.g. via a PreparedSubscriber or Event\Test\Started hook, before tearDown() runs) and cache it per-test, rather than reflecting on the (possibly torn-down) test case object inside Passed/Failed/Skipped handlers.
- Alternatively, fall back to Illuminate\Foundation\Application::getInstance() if the reflected $app property is null, if the static instance is still available at that point.
Repro: any Laravel 12 + Pest 4 + PHPUnit 12 project with itsgoingd/clockwork 5.3.5, tests.collect enabled, and the extension registered in phpunit.xml — no test metadata files are ever created in storage/clockwork, and no error/warning is surfaced.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read Clockwork/Support/Laravel/Tests/ClockworkExtension.php and PHPUnit's vendor/phpunit/phpunit/src/Framework/TestCase.php around lines 621–659 first, then trace the Passed, Failed, Skipped, and Started event subscribers. Confirm that the app remains available when test metadata is recorded under Laravel 12 with PHPUnit 12 and Pest 4, and verify that files are written to storage/clockwork.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- laravel, php
- Domain
- testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100