feat: allows to use test calls on before each calls

This commit is contained in:
Nuno Maduro
2023-05-01 22:18:45 +01:00
parent cddddc3ec1
commit 97898a0a8e
14 changed files with 118 additions and 26 deletions

View File

@ -50,7 +50,7 @@
}, },
"require-dev": { "require-dev": {
"pestphp/pest-dev-tools": "^2.7.0", "pestphp/pest-dev-tools": "^2.7.0",
"symfony/process": "^6.2.8" "symfony/process": "^6.2.10"
}, },
"minimum-stability": "stable", "minimum-stability": "stable",
"config": { "config": {

View File

@ -181,7 +181,7 @@ trait Testable
parent::setUp(); parent::setUp();
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename); $beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename)[1];
if ($this->__beforeEach instanceof Closure) { if ($this->__beforeEach instanceof Closure) {
$beforeEach = ChainableClosure::from($this->__beforeEach, $beforeEach); $beforeEach = ChainableClosure::from($this->__beforeEach, $beforeEach);

View File

@ -43,7 +43,7 @@ if (! function_exists('beforeEach')) {
/** /**
* Runs the given closure before each test in the current file. * Runs the given closure before each test in the current file.
* *
* @return HigherOrderTapProxy<TestCall|TestCase>|TestCall|mixed * @return HigherOrderTapProxy<TestCall|TestCase>|TestCall|TestCase|mixed
*/ */
function beforeEach(Closure $closure = null): BeforeEachCall function beforeEach(Closure $closure = null): BeforeEachCall
{ {

View File

@ -19,12 +19,17 @@ final class BeforeEachCall
/** /**
* Holds the before each closure. * Holds the before each closure.
*/ */
private readonly \Closure $closure; private readonly Closure $closure;
/** /**
* The calls that should be proxied. * The test call proxies.
*/ */
private readonly HigherOrderMessageCollection $proxies; private readonly HigherOrderMessageCollection $testCallProxies;
/**
* The test case proxies.
*/
private readonly HigherOrderMessageCollection $testCaseProxies;
/** /**
* Creates a new Pending Call. * Creates a new Pending Call.
@ -36,7 +41,8 @@ final class BeforeEachCall
) { ) {
$this->closure = $closure instanceof Closure ? $closure : NullClosure::create(); $this->closure = $closure instanceof Closure ? $closure : NullClosure::create();
$this->proxies = new HigherOrderMessageCollection(); $this->testCallProxies = new HigherOrderMessageCollection();
$this->testCaseProxies = new HigherOrderMessageCollection();
} }
/** /**
@ -44,13 +50,16 @@ final class BeforeEachCall
*/ */
public function __destruct() public function __destruct()
{ {
$proxies = $this->proxies; $testCaseProxies = $this->testCaseProxies;
$this->testSuite->beforeEach->set( $this->testSuite->beforeEach->set(
$this->filename, $this->filename,
ChainableClosure::from(function () use ($proxies): void { function (TestCall $testCall): void {
$proxies->chain($this); $this->testCallProxies->chain($testCall);
}, $this->closure) },
ChainableClosure::from(function () use ($testCaseProxies): void {
$testCaseProxies->chain($this);
}, $this->closure),
); );
} }
@ -61,7 +70,13 @@ final class BeforeEachCall
*/ */
public function __call(string $name, array $arguments): self public function __call(string $name, array $arguments): self
{ {
$this->proxies if (method_exists(TestCall::class, $name)) {
$this->testCallProxies->add(Backtrace::file(), Backtrace::line(), $name, $arguments);
return $this;
}
$this->testCaseProxies
->add(Backtrace::file(), Backtrace::line(), $name, $arguments); ->add(Backtrace::file(), Backtrace::line(), $name, $arguments);
return $this; return $this;

View File

@ -40,12 +40,15 @@ final class TestCall
*/ */
public function __construct( public function __construct(
private readonly TestSuite $testSuite, private readonly TestSuite $testSuite,
string $filename, private readonly string $filename,
string $description = null, string $description = null,
Closure $closure = null Closure $closure = null
) { ) {
$this->testCaseMethod = new TestCaseMethodFactory($filename, $description, $closure); $this->testCaseMethod = new TestCaseMethodFactory($filename, $description, $closure);
$this->descriptionLess = $description === null; $this->descriptionLess = $description === null;
$this->testSuite->beforeEach->get($filename)[0]($this);
} }
/** /**
@ -167,7 +170,7 @@ final class TestCall
$this->testCaseMethod $this->testCaseMethod
->chains ->chains
->addWhen($condition, Backtrace::file(), Backtrace::line(), 'markTestSkipped', [$message]); ->addWhen($condition, $this->filename, Backtrace::line(), 'markTestSkipped', [$message]);
return $this; return $this;
} }

View File

@ -14,27 +14,34 @@ use Pest\Support\NullClosure;
final class BeforeEachRepository final class BeforeEachRepository
{ {
/** /**
* @var array<string, Closure> * @var array<string, array{0: Closure, 1: Closure}>
*/ */
private array $state = []; private array $state = [];
/** /**
* Sets a before each closure. * Sets a before each closure.
*/ */
public function set(string $filename, Closure $closure): void public function set(string $filename, Closure $beforeEachTestCall, Closure $beforeEachTestCase): void
{ {
if (array_key_exists($filename, $this->state)) { if (array_key_exists($filename, $this->state)) {
throw new BeforeEachAlreadyExist($filename); throw new BeforeEachAlreadyExist($filename);
} }
$this->state[$filename] = $closure; $this->state[$filename] = [$beforeEachTestCall, $beforeEachTestCase];
} }
/** /**
* Gets a before each closure by the given filename. * Gets a before each closure by the given filename.
*
* @return array{0: Closure, 1: Closure}
*/ */
public function get(string $filename): Closure public function get(string $filename): array
{ {
return $this->state[$filename] ?? NullClosure::create(); $closures = $this->state[$filename] ?? [];
return [
$closures[0] ?? NullClosure::create(),
$closures[1] ?? NullClosure::create(),
];
} }
} }

View File

@ -104,8 +104,6 @@ final class Backtrace
{ {
$trace = debug_backtrace(self::BACKTRACE_OPTIONS)[1]; $trace = debug_backtrace(self::BACKTRACE_OPTIONS)[1];
assert(array_key_exists('line', $trace)); return $trace['line'] ?? 0;
return $trace['line'];
} }
} }

View File

@ -22,6 +22,22 @@
✓ it gets executed before each test ✓ it gets executed before each test
✓ it gets executed before each test once again ✓ it gets executed before each test once again
PASS Tests\Features\BeforeEachProxiesToTestCallWithExpectations
✓ runs 1
✓ runs 2
✓ runs 3
WARN Tests\Features\BeforeEachProxiesToTestCallWithSkip
- does not run 1
- does not run 2
- does not run 3
TODO Tests\Features\BeforeEachProxiesToTestCallWithTodo - 4 todos
↓ is marked as todo 1
↓ is marked as todo 2
↓ is marked as todo 3
↓ shouldBeMarkedAsTodo
WARN Tests\Features\Coverage WARN Tests\Features\Coverage
✓ it has plugin ✓ it has plugin
- it adds coverage if --coverage exist → Coverage is not available - it adds coverage if --coverage exist → Coverage is not available
@ -1017,4 +1033,4 @@
PASS Tests\Visual\Version PASS Tests\Visual\Version
✓ visual snapshot of help command output ✓ visual snapshot of help command output
Tests: 1 deprecated, 3 warnings, 4 incomplete, 1 notice, 4 todos, 14 skipped, 711 passed (1719 assertions) Tests: 1 deprecated, 3 warnings, 4 incomplete, 1 notice, 8 todos, 17 skipped, 714 passed (1722 assertions)

View File

@ -1,3 +1,9 @@
TODO Tests\Features\BeforeEachProxiesToTestCallWithTodo - 4 todos
↓ is marked as todo 1
↓ is marked as todo 2
↓ is marked as todo 3
↓ shouldBeMarkedAsTodo
TODO Tests\Features\DatasetsTests - 1 todo TODO Tests\Features\DatasetsTests - 1 todo
↓ forbids to define tests in Datasets dirs and Datasets.php files ↓ forbids to define tests in Datasets dirs and Datasets.php files
@ -9,4 +15,4 @@
PASS Tests\CustomTestCase\ExecutedTest PASS Tests\CustomTestCase\ExecutedTest
✓ that gets executed ✓ that gets executed
Tests: 4 todos, 1 passed (1 assertions) Tests: 8 todos, 1 passed (1 assertions)

View File

@ -0,0 +1,15 @@
<?php
beforeEach()->expect(true)->toBeTrue();
test('runs 1', function () {
// This test did performs assertions...
});
test('runs 2', function () {
// This test did performs assertions...
});
test('runs 3', function () {
// This test did performs assertions...
});

View File

@ -0,0 +1,15 @@
<?php
beforeEach()->skip();
test('does not run 1', function () {
$this->fail('This test should not run');
});
test('does not run 2', function () {
$this->fail('This test should not run');
});
test('does not run 3', function () {
$this->fail('This test should not run');
});

View File

@ -0,0 +1,15 @@
<?php
beforeEach()->todo();
test('is marked as todo 1', function () {
$this->fail('This test should not run');
});
test('is marked as todo 2', function () {
$this->fail('This test should not run');
});
test('is marked as todo 3');
test()->shouldBeMarkedAsTodo();

View File

@ -18,7 +18,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: 1 deprecated, 3 warnings, 4 incomplete, 1 notice, 4 todos, 11 skipped, 699 passed (1704 assertions)') ->toContain('Tests: 1 deprecated, 3 warnings, 4 incomplete, 1 notice, 8 todos, 14 skipped, 702 passed (1707 assertions)')
->toContain('Parallel: 3 processes'); ->toContain('Parallel: 3 processes');
})->skipOnWindows(); })->skipOnWindows();

View File

@ -11,7 +11,9 @@ $run = function (string $target, bool $parallel) {
expect($process->getExitCode())->toBe(0); expect($process->getExitCode())->toBe(0);
return preg_replace('#\\x1b[[][^A-Za-z]*[A-Za-z]#', '', $process->getOutput()); $outputContent = preg_replace('#\\x1b[[][^A-Za-z]*[A-Za-z]#', '', $process->getOutput());
return $outputContent;
}; };
$snapshot = function ($name) { $snapshot = function ($name) {