mirror of
https://github.com/pestphp/pest.git
synced 2026-04-21 22:47:27 +02:00
fix: stacktrace with nested with calls
This commit is contained in:
@ -74,7 +74,7 @@ final class DescribeCall
|
|||||||
*/
|
*/
|
||||||
public function __call(string $name, array $arguments): self
|
public function __call(string $name, array $arguments): self
|
||||||
{
|
{
|
||||||
if (! $this->currentBeforeEachCall instanceof \Pest\PendingCalls\BeforeEachCall) {
|
if (! $this->currentBeforeEachCall instanceof BeforeEachCall) {
|
||||||
$this->currentBeforeEachCall = new BeforeEachCall(TestSuite::getInstance(), $this->filename);
|
$this->currentBeforeEachCall = new BeforeEachCall(TestSuite::getInstance(), $this->filename);
|
||||||
|
|
||||||
$this->currentBeforeEachCall->describing = array_merge(
|
$this->currentBeforeEachCall->describing = array_merge(
|
||||||
|
|||||||
@ -23,7 +23,9 @@ final class Backtrace
|
|||||||
$current = null;
|
$current = null;
|
||||||
|
|
||||||
foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) {
|
foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) {
|
||||||
assert(array_key_exists(self::FILE, $trace));
|
if (array_key_exists(self::FILE, $trace) === false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$traceFile = str_replace(DIRECTORY_SEPARATOR, '/', $trace[self::FILE]);
|
$traceFile = str_replace(DIRECTORY_SEPARATOR, '/', $trace[self::FILE]);
|
||||||
|
|
||||||
|
|||||||
@ -95,6 +95,48 @@
|
|||||||
PASS Tests\Features\Covers\TraitCoverage
|
PASS Tests\Features\Covers\TraitCoverage
|
||||||
✓ it uses the correct PHPUnit attribute for trait
|
✓ it uses the correct PHPUnit attribute for trait
|
||||||
|
|
||||||
|
PASS Tests\Features\DatasetMethodChaining
|
||||||
|
✓ beforeEach()->with() applies dataset to tests → receives the dataset value with (10)
|
||||||
|
✓ beforeEach()->with() applies dataset to tests → it also receives the dataset value in it() with (10)
|
||||||
|
✓ beforeEach()->with() with multiple dataset values → receives each value from the dataset with (1)
|
||||||
|
✓ beforeEach()->with() with multiple dataset values → receives each value from the dataset with (2)
|
||||||
|
✓ beforeEach()->with() with multiple dataset values → receives each value from the dataset with (3)
|
||||||
|
✓ beforeEach()->with() with keyed dataset → receives keyed dataset values with dataset "first"
|
||||||
|
✓ beforeEach()->with() with keyed dataset → receives keyed dataset values with dataset "second"
|
||||||
|
✓ beforeEach()->with() with closure dataset → receives values from closure dataset with (100)
|
||||||
|
✓ beforeEach()->with() with closure dataset → receives values from closure dataset with (200)
|
||||||
|
✓ describe()->with() passes dataset to tests → receives the dataset value with (42)
|
||||||
|
✓ describe()->with() passes dataset to tests → it also receives it in it() with (42)
|
||||||
|
✓ describe()->with() with multiple values → receives each value with (5)
|
||||||
|
✓ describe()->with() with multiple values → receives each value with (10)
|
||||||
|
✓ describe()->with() with multiple values → receives each value with (15)
|
||||||
|
✓ describe()->with() with keyed dataset → receives keyed values with dataset "alpha"
|
||||||
|
✓ describe()->with() with keyed dataset → receives keyed values with dataset "beta"
|
||||||
|
✓ describe()->with() with closure dataset → receives closure dataset values with (7)
|
||||||
|
✓ describe()->with() with closure dataset → receives closure dataset values with (14)
|
||||||
|
✓ outer with dataset → inner without dataset → inherits outer dataset with (1)
|
||||||
|
✓ nested describe blocks with datasets at multiple levels → level 1 → receives level 1 dataset with (10)
|
||||||
|
✓ nested describe blocks with datasets at multiple levels → level 1 → level 2 → receives datasets from all ancestor levels with (10) / (20)
|
||||||
|
✓ deeply nested describe with datasets → a → b → c → receives all ancestor datasets with (1) / (2) / (3)
|
||||||
|
✓ beforeEach()->with() combined with test->with() → receives both datasets as cross product with (10) / (1)
|
||||||
|
✓ beforeEach()->with() combined with test->with() → receives both datasets as cross product with (10) / (2)
|
||||||
|
✓ describe()->with() combined with test->with() → receives both datasets with (5) / (50)
|
||||||
|
✓ describe()->with() combined with test->with() → receives both datasets with (5) / (60)
|
||||||
|
✓ beforeEach closure and beforeEach()->with() coexist → has both the closure state and dataset with (99)
|
||||||
|
✓ beforeEach()->with() does not interfere with closure hooks → closures run in order and dataset is applied with (42)
|
||||||
|
✓ first describe with dataset → gets its own dataset with (111)
|
||||||
|
✓ second describe with different dataset → gets its own dataset, not the sibling with (222)
|
||||||
|
✓ third describe without dataset → has no dataset leaking from siblings
|
||||||
|
✓ describe()->with() with beforeEach closure → both hook and dataset work with (77)
|
||||||
|
✓ describe()->with() with afterEach closure → dataset is available and afterEach runs with (88)
|
||||||
|
✓ multiple tests share the same beforeEach dataset → first test gets the dataset with (33)
|
||||||
|
✓ multiple tests share the same beforeEach dataset → second test also gets the dataset with (33)
|
||||||
|
✓ multiple tests share the same beforeEach dataset → it third test with it() also gets the dataset with (33)
|
||||||
|
✓ outer describe → inner describe with dataset on hook → inherits outer beforeEach and has inner dataset with (55)
|
||||||
|
✓ outer describe → outer test is unaffected by inner dataset
|
||||||
|
✓ describe()->with() preserves depends → first with (9)
|
||||||
|
✓ describe()->with() preserves depends → second with (9)
|
||||||
|
|
||||||
PASS Tests\Features\DatasetsTests - 1 todo
|
PASS Tests\Features\DatasetsTests - 1 todo
|
||||||
✓ it throws exception if dataset does not exist
|
✓ it throws exception if dataset does not exist
|
||||||
✓ it throws exception if dataset already exist
|
✓ it throws exception if dataset already exist
|
||||||
@ -215,6 +257,20 @@
|
|||||||
✓ it may be used with high order after describe block with dataset "formal"
|
✓ it may be used with high order after describe block with dataset "formal"
|
||||||
✓ it may be used with high order after describe block with dataset "informal"
|
✓ it may be used with high order after describe block with dataset "informal"
|
||||||
✓ after describe block with named dataset with ('after')
|
✓ after describe block with named dataset with ('after')
|
||||||
|
✓ named parameters match by parameter name with ('Taylor', 'taylor@laravel.com')
|
||||||
|
✓ named parameters work with multiple dataset items with ('Taylor', 'taylor@laravel.com')
|
||||||
|
✓ named parameters work with multiple dataset items with ('James', 'james@laravel.com')
|
||||||
|
✓ named parameters work in different order than closure params with ('a', 'b', 'c')
|
||||||
|
✓ named parameters work with named dataset keys with dataset "taylor"
|
||||||
|
✓ named parameters work with named dataset keys with dataset "james"
|
||||||
|
✓ named parameters work with closures that should be resolved with (Closure Object (), Closure Object ())
|
||||||
|
✓ named parameters work with closure type hints with ('Taylor', Closure Object ())
|
||||||
|
✓ named parameters work with registered datasets with ('Taylor', 'taylor@laravel.com')
|
||||||
|
✓ named parameters work with registered datasets with ('James', 'james@laravel.com')
|
||||||
|
✓ named parameters work with bound closure returning associative array with (Closure Object ())
|
||||||
|
✓ dataset items can mix named and sequential styles with ('Taylor', 'taylor@laravel.com')
|
||||||
|
✓ dataset items can mix named and sequential styles with ('James', 'james@laravel.com') #1
|
||||||
|
✓ dataset items can mix named and sequential styles with ('James', 'james@laravel.com') #2
|
||||||
|
|
||||||
PASS Tests\Features\Depends
|
PASS Tests\Features\Depends
|
||||||
✓ first
|
✓ first
|
||||||
@ -1813,4 +1869,4 @@
|
|||||||
✓ pass with dataset with ('my-datas-set-value')
|
✓ pass with dataset with ('my-datas-set-value')
|
||||||
✓ within describe → pass with dataset with ('my-datas-set-value')
|
✓ within describe → pass with dataset with ('my-datas-set-value')
|
||||||
|
|
||||||
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 39 todos, 35 skipped, 1211 passed (2847 assertions)
|
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 39 todos, 35 skipped, 1265 passed (2925 assertions)
|
||||||
287
tests/Features/DatasetMethodChaining.php
Normal file
287
tests/Features/DatasetMethodChaining.php
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for dataset method chaining with hooks and describe blocks.
|
||||||
|
*
|
||||||
|
* Covers the fix from PR #1565: beforeEach()->with(), describe()->with(),
|
||||||
|
* and nested describe blocks with datasets.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// beforeEach()->with() inside describe blocks
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('beforeEach()->with() applies dataset to tests', function () {
|
||||||
|
beforeEach()->with([10]);
|
||||||
|
|
||||||
|
test('receives the dataset value', function ($value) {
|
||||||
|
expect($value)->toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('also receives the dataset value in it()', function ($value) {
|
||||||
|
expect($value)->toBe(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('beforeEach()->with() with multiple dataset values', function () {
|
||||||
|
beforeEach()->with([1, 2, 3]);
|
||||||
|
|
||||||
|
test('receives each value from the dataset', function ($value) {
|
||||||
|
expect($value)->toBeIn([1, 2, 3]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('beforeEach()->with() with keyed dataset', function () {
|
||||||
|
beforeEach()->with(['first' => [10], 'second' => [20]]);
|
||||||
|
|
||||||
|
test('receives keyed dataset values', function ($value) {
|
||||||
|
expect($value)->toBeIn([10, 20]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('beforeEach()->with() with closure dataset', function () {
|
||||||
|
beforeEach()->with(function () {
|
||||||
|
yield [100];
|
||||||
|
yield [200];
|
||||||
|
});
|
||||||
|
|
||||||
|
test('receives values from closure dataset', function ($value) {
|
||||||
|
expect($value)->toBeIn([100, 200]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// describe()->with() method chaining
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('describe()->with() passes dataset to tests', function () {
|
||||||
|
test('receives the dataset value', function ($value) {
|
||||||
|
expect($value)->toBe(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('also receives it in it()', function ($value) {
|
||||||
|
expect($value)->toBe(42);
|
||||||
|
});
|
||||||
|
})->with([42]);
|
||||||
|
|
||||||
|
describe('describe()->with() with multiple values', function () {
|
||||||
|
test('receives each value', function ($value) {
|
||||||
|
expect($value)->toBeIn([5, 10, 15]);
|
||||||
|
});
|
||||||
|
})->with([5, 10, 15]);
|
||||||
|
|
||||||
|
describe('describe()->with() with keyed dataset', function () {
|
||||||
|
test('receives keyed values', function ($value) {
|
||||||
|
expect($value)->toBeIn([100, 200]);
|
||||||
|
});
|
||||||
|
})->with(['alpha' => [100], 'beta' => [200]]);
|
||||||
|
|
||||||
|
describe('describe()->with() with closure dataset', function () {
|
||||||
|
test('receives closure dataset values', function ($value) {
|
||||||
|
expect($value)->toBeIn([7, 14]);
|
||||||
|
});
|
||||||
|
})->with(function () {
|
||||||
|
yield [7];
|
||||||
|
yield [14];
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Nested describe blocks with datasets
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('outer with dataset', function () {
|
||||||
|
describe('inner without dataset', function () {
|
||||||
|
test('inherits outer dataset', function (...$args) {
|
||||||
|
expect($args)->toBe([1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})->with([1]);
|
||||||
|
|
||||||
|
describe('nested describe blocks with datasets at multiple levels', function () {
|
||||||
|
describe('level 1', function () {
|
||||||
|
test('receives level 1 dataset', function (...$args) {
|
||||||
|
expect($args)->toBe([10]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('level 2', function () {
|
||||||
|
test('receives datasets from all ancestor levels', function (...$args) {
|
||||||
|
expect($args)->toBe([10, 20]);
|
||||||
|
});
|
||||||
|
})->with([20]);
|
||||||
|
})->with([10]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('deeply nested describe with datasets', function () {
|
||||||
|
describe('a', function () {
|
||||||
|
describe('b', function () {
|
||||||
|
describe('c', function () {
|
||||||
|
test('receives all ancestor datasets', function (...$args) {
|
||||||
|
expect($args)->toBe([1, 2, 3]);
|
||||||
|
});
|
||||||
|
})->with([3]);
|
||||||
|
})->with([2]);
|
||||||
|
})->with([1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Combining hook datasets with test-level datasets
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('beforeEach()->with() combined with test->with()', function () {
|
||||||
|
beforeEach()->with([10]);
|
||||||
|
|
||||||
|
test('receives both datasets as cross product', function ($hookValue, $testValue) {
|
||||||
|
expect($hookValue)->toBe(10);
|
||||||
|
expect($testValue)->toBeIn([1, 2]);
|
||||||
|
})->with([1, 2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('describe()->with() combined with test->with()', function () {
|
||||||
|
test('receives both datasets', function ($describeValue, $testValue) {
|
||||||
|
expect($describeValue)->toBe(5);
|
||||||
|
expect($testValue)->toBeIn([50, 60]);
|
||||||
|
})->with([50, 60]);
|
||||||
|
})->with([5]);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// beforeEach()->with() combined with beforeEach closure
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('beforeEach closure and beforeEach()->with() coexist', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->setupValue = 'initialized';
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach()->with([99]);
|
||||||
|
|
||||||
|
test('has both the closure state and dataset', function ($value) {
|
||||||
|
expect($this->setupValue)->toBe('initialized');
|
||||||
|
expect($value)->toBe(99);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('beforeEach()->with() does not interfere with closure hooks', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->counter = 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->counter++;
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach()->with([42]);
|
||||||
|
|
||||||
|
test('closures run in order and dataset is applied', function ($value) {
|
||||||
|
expect($this->counter)->toBe(2);
|
||||||
|
expect($value)->toBe(42);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Dataset isolation between describe blocks
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('first describe with dataset', function () {
|
||||||
|
beforeEach()->with([111]);
|
||||||
|
|
||||||
|
test('gets its own dataset', function ($value) {
|
||||||
|
expect($value)->toBe(111);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('second describe with different dataset', function () {
|
||||||
|
beforeEach()->with([222]);
|
||||||
|
|
||||||
|
test('gets its own dataset, not the sibling', function ($value) {
|
||||||
|
expect($value)->toBe(222);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('third describe without dataset', function () {
|
||||||
|
test('has no dataset leaking from siblings', function () {
|
||||||
|
expect(true)->toBeTrue();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// describe()->with() combined with beforeEach hooks
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('describe()->with() with beforeEach closure', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->hookRan = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('both hook and dataset work', function ($value) {
|
||||||
|
expect($this->hookRan)->toBeTrue();
|
||||||
|
expect($value)->toBe(77);
|
||||||
|
});
|
||||||
|
})->with([77]);
|
||||||
|
|
||||||
|
describe('describe()->with() with afterEach closure', function () {
|
||||||
|
afterEach(function () {
|
||||||
|
expect($this->value)->toBe(88);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dataset is available and afterEach runs', function ($value) {
|
||||||
|
$this->value = $value;
|
||||||
|
expect($value)->toBe(88);
|
||||||
|
});
|
||||||
|
})->with([88]);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Multiple tests in a describe with beforeEach()->with()
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('multiple tests share the same beforeEach dataset', function () {
|
||||||
|
beforeEach()->with([33]);
|
||||||
|
|
||||||
|
test('first test gets the dataset', function ($value) {
|
||||||
|
expect($value)->toBe(33);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('second test also gets the dataset', function ($value) {
|
||||||
|
expect($value)->toBe(33);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('third test with it() also gets the dataset', function ($value) {
|
||||||
|
expect($value)->toBe(33);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Nested describe with beforeEach()->with() at inner level
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('outer describe', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->outer = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('inner describe with dataset on hook', function () {
|
||||||
|
beforeEach()->with([55]);
|
||||||
|
|
||||||
|
test('inherits outer beforeEach and has inner dataset', function ($value) {
|
||||||
|
expect($this->outer)->toBeTrue();
|
||||||
|
expect($value)->toBe(55);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('outer test is unaffected by inner dataset', function () {
|
||||||
|
expect($this->outer)->toBeTrue();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// describe()->with() with depends
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
describe('describe()->with() preserves depends', function () {
|
||||||
|
test('first', function ($value) {
|
||||||
|
expect($value)->toBe(9);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('second', function ($value) {
|
||||||
|
expect($value)->toBe(9);
|
||||||
|
})->depends('first');
|
||||||
|
})->with([9]);
|
||||||
@ -16,7 +16,7 @@ $run = function () {
|
|||||||
|
|
||||||
test('parallel', function () use ($run) {
|
test('parallel', function () use ($run) {
|
||||||
expect($run('--exclude-group=integration'))
|
expect($run('--exclude-group=integration'))
|
||||||
->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 3 notices, 39 todos, 26 skipped, 1196 passed (2809 assertions)')
|
->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 3 notices, 39 todos, 26 skipped, 1250 passed (2887 assertions)')
|
||||||
->toContain('Parallel: 3 processes');
|
->toContain('Parallel: 3 processes');
|
||||||
})->skipOnWindows();
|
})->skipOnWindows();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user