fix: run pest()->afterAll() hooks by flushing statics after tearDownAfterClass()#1753
Open
sonalidudhia wants to merge 1 commit into
Open
fix: run pest()->afterAll() hooks by flushing statics after tearDownAfterClass()#1753sonalidudhia wants to merge 1 commit into
pest()->afterAll() hooks by flushing statics after tearDownAfterClass()#1753sonalidudhia wants to merge 1 commit into
Conversation
…wnAfterClass()` Fixes pestphp#1694 Testable::tearDown()'s finally block flushed the afterAll static after every test via TestCaseMethodFactory::tearDown(), so by the time PHPUnit called tearDownAfterClass() (which runs after the last test's tearDown()) the hook had already been nulled out. The per-test flush was redundant: TestCaseMethodFactory::setUp() already flushes statics before re-applying proxies for the next test. Move the flush into tearDownAfterClass()'s finally block instead, so it happens after the hook actually runs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
sonalidudhia
marked this pull request as ready for review
July 15, 2026 12:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix: run
pest()->afterAll()hooks by flushing statics aftertearDownAfterClass()Fixes #1694
What was happening
Hooks registered via
pest()->afterAll()silently never executed. The reporterobserved the hook closure showing up as a
NullClosureinsidetearDownAfterClass().Root cause
Testable::tearDown()'sfinallyblock called$method->tearDown($this)→TestCaseMethodFactory::tearDown()→$concrete::flush(), which nulls the staticself::$__afterAllafterevery test. PHPUnit calls
tearDownAfterClass()after the last test'stearDown(), so by the time it runs, the static holding theafterAllhookhad already been wiped — leaving the
NullClosurethe reporter saw.beforeAlldoesn't have this problem because statics are populated atinstance initialization, which happens before
setUpBeforeClass()everconsumes them — there's no intervening per-test flush in that direction.
The per-test flush was also redundant:
TestCaseMethodFactory::setUp()already flushes statics before re-applying the
Uses-level hook proxies forthe next test, which is what actually prevents hooks from chaining
duplicates across tests, datasets, and repetitions.
The fix
TestCaseMethodFactory::tearDown()and its call site in
Testable::tearDown()) —setUp()'s pre-proxy flushalready covers hygiene between tests.
tearDownAfterClass()'sfinallyblock, after theafterAllhook chain runs, so the static resets at the correct point evenif a hook throws. Hook ordering (uses-hooks chained before the file-level
afterAll, thenparent::tearDownAfterClass()) is unchanged.Tests
tests/Hooks/hadBeforeAllTest.php,BeforeEachTest.php, andAfterEachTest.php, but noAfterAllTest.php— that gap is why thisregression went unnoticed. Added
tests/Hooks/AfterAllTest.php.A conventional in-test
expect()assertion can't verify this hook actuallyran, because
afterAllonly executes after every test in the class hasfinished — there's no later test to observe it from. The test instead mirrors
the shutdown-assertion pattern used in
tests/Features/TestCycle.php:register_shutdown_function()asserts the hook fired exactly once, after thewhole process (including
tearDownAfterClass()) has completed.Evidence
On 4.x HEAD (test only, source unpatched):
Pest's own reporter shows green (the per-test assertions genuinely pass — the
bug is that
afterAllnever runs, so there's nothing in-test to fail on).The process still exits
255: PHPUnit'sregister_shutdown_functionassertion fails after the runner has already printed its summary, because
afterAllInFilestayed0— the hook was never invoked.With this fix:
As a side effect, the two
pest()->in('Hooks')->afterAll()hooks declared intests/Pest.php(which target every file in this directory, including thenew one) now actually execute for the first time — their internal
expect()assertions pass under this fix.