From 26345fd9f4aad848773834b91df347fe1018e3a9 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 5 Nov 2025 17:46:52 +0100 Subject: [PATCH] fix: dataset inheritance with method chaining (beforeEach()->with(), describe()->with()) Fixes issue where datasets were not applied when using method chaining patterns like beforeEach()->with([...]) or describe()->with([...]) inside nested describe blocks. Root cause: Multiple functions were using Backtrace::file() which returns the immediate caller's filename. This breaks when called through method chaining because the backtrace returns internal Pest files instead of the test file. Solution: Use Backtrace::testFile() which walks the entire backtrace to find the actual test file being executed. This matches the pattern already used by test() and describe() functions. Changes in src/Functions.php: - beforeEach(): Use testFile() to fix beforeEach()->with() pattern - afterEach(): Use testFile() for consistency with beforeEach() - beforeAll(): Use testFile() for better error messages - afterAll(): Use testFile() for better error messages - pest(): Use testFile() to fix pest()->beforeEach() pattern - uses(): Use testFile() for consistency with pest() - covers(): Use testFile() for correct test file context - mutates(): Use testFile() for correct test file context Changes in src/PendingCalls/DescribeCall.php: - __destruct(): Force BeforeEachCall destructor before test creation - __call(): Use $this->filename instead of Backtrace, more efficient - __call(): Properly merge describing context for nested describe blocks Fixes patterns: - beforeEach()->with([...]) - describe()->with([...]) - pest()->beforeEach()->with([...] Tests passing: - tests/Features/Describe.php (all dataset tests) - tests/Hooks/BeforeEachTest.php (global hook execution) - tests/Features/Expect/toMatchSnapshot.php (28 tests) --- src/Functions.php | 16 ++++++++-------- src/PendingCalls/DescribeCall.php | 16 ++++++++++------ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/Functions.php b/src/Functions.php index 0ed631f3..5bc916a2 100644 --- a/src/Functions.php +++ b/src/Functions.php @@ -48,7 +48,7 @@ function expect(mixed $value = null): Expectation function beforeAll(Closure $closure): void { if (DescribeCall::describing() !== []) { - $filename = Backtrace::file(); + $filename = Backtrace::testFile(); throw new BeforeAllWithinDescribe($filename); } @@ -67,7 +67,7 @@ function beforeAll(Closure $closure): void */ function beforeEach(?Closure $closure = null): BeforeEachCall { - $filename = Backtrace::file(); + $filename = Backtrace::testFile(); return new BeforeEachCall(TestSuite::getInstance(), $filename, $closure); } @@ -112,7 +112,7 @@ function describe(string $description, Closure $tests): DescribeCall */ function uses(string ...$classAndTraits): UsesCall { - $filename = Backtrace::file(); + $filename = Backtrace::testFile(); return new UsesCall($filename, array_values($classAndTraits)); } @@ -124,7 +124,7 @@ function uses(string ...$classAndTraits): UsesCall */ function pest(): Configuration { - return new Configuration(Backtrace::file()); + return new Configuration(Backtrace::testFile()); } } @@ -197,7 +197,7 @@ function todo(string $description): TestCall */ function afterEach(?Closure $closure = null): AfterEachCall { - $filename = Backtrace::file(); + $filename = Backtrace::testFile(); return new AfterEachCall(TestSuite::getInstance(), $filename, $closure); } @@ -210,7 +210,7 @@ function afterEach(?Closure $closure = null): AfterEachCall function afterAll(Closure $closure): void { if (DescribeCall::describing() !== []) { - $filename = Backtrace::file(); + $filename = Backtrace::testFile(); throw new AfterAllWithinDescribe($filename); } @@ -227,7 +227,7 @@ function afterAll(Closure $closure): void */ function covers(array|string ...$classesOrFunctions): void { - $filename = Backtrace::file(); + $filename = Backtrace::testFile(); $beforeEachCall = (new BeforeEachCall(TestSuite::getInstance(), $filename)); @@ -256,7 +256,7 @@ function covers(array|string ...$classesOrFunctions): void */ function mutates(array|string ...$targets): void { - $filename = Backtrace::file(); + $filename = Backtrace::testFile(); $beforeEachCall = (new BeforeEachCall(TestSuite::getInstance(), $filename)); $beforeEachCall->group('__pest_mutate_only'); diff --git a/src/PendingCalls/DescribeCall.php b/src/PendingCalls/DescribeCall.php index 08ebc15e..d96b4a3c 100644 --- a/src/PendingCalls/DescribeCall.php +++ b/src/PendingCalls/DescribeCall.php @@ -5,7 +5,6 @@ namespace Pest\PendingCalls; use Closure; -use Pest\Support\Backtrace; use Pest\Support\Description; use Pest\TestSuite; @@ -53,7 +52,11 @@ public static function describing(): array */ public function __destruct() { - unset($this->currentBeforeEachCall); + // Ensure BeforeEachCall destructs before creating tests + // by moving to local scope and clearing the reference + $beforeEach = $this->currentBeforeEachCall; + $this->currentBeforeEachCall = null; + unset($beforeEach); // Trigger destructor immediately self::$describing[] = $this->description; @@ -71,12 +74,13 @@ public function __destruct() */ public function __call(string $name, array $arguments): self { - $filename = Backtrace::file(); - if (! $this->currentBeforeEachCall instanceof \Pest\PendingCalls\BeforeEachCall) { - $this->currentBeforeEachCall = new BeforeEachCall(TestSuite::getInstance(), $filename); + $this->currentBeforeEachCall = new BeforeEachCall(TestSuite::getInstance(), $this->filename); - $this->currentBeforeEachCall->describing[] = $this->description; + $this->currentBeforeEachCall->describing = array_merge( + DescribeCall::describing(), + [$this->description] + ); } $this->currentBeforeEachCall->{$name}(...$arguments);