diff --git a/src/Concerns/TestCase.php b/src/Concerns/TestCase.php index cc6a5f8d..8469b4b8 100644 --- a/src/Concerns/TestCase.php +++ b/src/Concerns/TestCase.php @@ -5,7 +5,9 @@ declare(strict_types=1); namespace Pest\Concerns; use Closure; +use Pest\Repositories\BeforeEachRepository; use Pest\Support\ExceptionTrace; +use Pest\Support\ChainableClosure; use Pest\TestSuite; use PHPUnit\Framework\ExecutionOrderDependency; use PHPUnit\Util\Test; @@ -34,13 +36,29 @@ trait TestCase */ private $__test; + /** + * Holds a global/shared beforeEach ("set up") closure if one has been + * defined. + * + * @var Closure|null + */ + private $beforeEach = null; + + /** + * Holds a global/shared afterEach ("tear down") closure if one has been + * defined. + * + * @var Closure|null + */ + private $afterEach = null; + /** * Creates a new instance of the test case. */ public function __construct(Closure $test, string $description, array $data) { $this->__test = $test; - $this->__description = $description; + $this->__description = $description; parent::__construct('__test', $data); } @@ -55,15 +73,6 @@ trait TestCase $this->setGroups($groups); } - /** - * Add a shared/"global" before each test hook that will execute **before** - * the test defined `beforeEach` hook. - */ - public function addBeforeEach(?Closure $hook): void - { - $this->beforeEach = $hook; - } - /** * Add dependencies to the test case and map them to instances of ExecutionOrderDependency. */ @@ -82,6 +91,38 @@ trait TestCase $this->setDependencies($tests); } + /** + * Add a shared/"global" before each test hook that will execute **before** + * the test defined `beforeEach` hook. + */ + public function addBeforeEach(?Closure $hook): void + { + $this->addHook('beforeEach', $hook); + } + + /** + * Add a shared/"global" after each test hook that will execute **before** + * the test defined `afterEach` hook. + */ + public function addAfterEach(?Closure $hook): void + { + $this->addHook('afterEach', $hook); + } + + /** + * Add a shared/global hook and compose them if more than one is passed. + */ + private function addHook(string $property, ?Closure $hook): void + { + if (!$hook) { + return; + } + + $this->{$property} = ($this->{$property} instanceof Closure) + ? ChainableClosure::from($this->{$property}, $hook) + : $hook; + } + /** * Returns the test case name. Note that, in Pest * we ignore withDataset argument as the description @@ -130,12 +171,12 @@ trait TestCase parent::setUp(); - if ($this->beforeEach instanceof Closure) { - $this->__callClosure($this->beforeEach, func_get_args()); - } - $beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename); + if ($this->beforeEach instanceof Closure) { + $beforeEach = ChainableClosure::from($this->beforeEach, $beforeEach); + } + $this->__callClosure($beforeEach, func_get_args()); } @@ -146,6 +187,10 @@ trait TestCase { $afterEach = TestSuite::getInstance()->afterEach->get(self::$__filename); + if ($this->afterEach instanceof Closure) { + $afterEach = ChainableClosure::from($this->afterEach, $afterEach); + } + $this->__callClosure($afterEach, func_get_args()); parent::tearDown(); diff --git a/src/PendingObjects/UsesCall.php b/src/PendingObjects/UsesCall.php index d0b33b38..0d56a4cc 100644 --- a/src/PendingObjects/UsesCall.php +++ b/src/PendingObjects/UsesCall.php @@ -16,9 +16,16 @@ final class UsesCall /** * Contains a global before each hook closure to be executed. * - * @var Closure + * Array indices here matter. They are mapped as follows: + * + * - `0` => `beforeAll` + * - `1` => `beforeEach` + * - `2` => `afterEach` + * - `3` => `afterAll` + * + * @var array */ - private $beforeEach; + private $hooks = []; /** * Holds the class and traits. @@ -106,11 +113,41 @@ final class UsesCall } /** - * Sets the global beforeEach test hook + * Sets the global beforeAll test hook. + */ + public function beforeAll(Closure $hook): UsesCall + { + $this->hooks[0] = $hook; + + return $this; + } + + /** + * Sets the global beforeEach test hook. */ public function beforeEach(Closure $hook): UsesCall { - $this->beforeEach = $hook; + $this->hooks[1] = $hook; + + return $this; + } + + /** + * Sets the global afterEach test hook. + */ + public function afterEach(Closure $hook): UsesCall + { + $this->hooks[2] = $hook; + + return $this; + } + + /** + * Sets the global afterAll test hook. + */ + public function afterAll(Closure $hook): UsesCall + { + $this->hooks[3] = $hook; return $this; } @@ -124,7 +161,7 @@ final class UsesCall $this->classAndTraits, $this->groups, $this->targets, - $this->beforeEach, + $this->hooks, ); } } diff --git a/src/Repositories/TestRepository.php b/src/Repositories/TestRepository.php index f8107247..d9b5406d 100644 --- a/src/Repositories/TestRepository.php +++ b/src/Repositories/TestRepository.php @@ -47,9 +47,9 @@ final class TestRepository }; foreach ($this->uses as $path => $uses) { - [$classOrTraits, $groups, $beforeEach] = $uses; + [$classOrTraits, $groups, $hooks] = $uses; - $setClassName = function (TestCaseFactory $testCase, string $key) use ($path, $classOrTraits, $groups, $startsWith, $beforeEach): void { + $setClassName = function (TestCaseFactory $testCase, string $key) use ($path, $classOrTraits, $groups, $startsWith, $hooks): void { [$filename] = explode('@', $key); if ((!is_dir($path) && $filename === $path) || (is_dir($path) && $startsWith($filename, $path))) { @@ -65,8 +65,11 @@ final class TestRepository } // IDEA: Consider set the real lines on these. - $testCase->factoryProxies->add($filename, 0, 'addBeforeEach', [$beforeEach]); $testCase->factoryProxies->add($filename, 0, 'addGroups', [$groups]); + // $testCase->factoryProxies->add($filename, 0, 'addBeforeAll', [$hooks[0] ?? null]); + $testCase->factoryProxies->add($filename, 0, 'addBeforeEach', [$hooks[1] ?? null]); + $testCase->factoryProxies->add($filename, 0, 'addAfterEach', [$hooks[2] ?? null]); + // $testCase->factoryProxies->add($filename, 0, 'addAfterAll', [$hooks[3] ?? null]); } }; @@ -96,9 +99,9 @@ final class TestRepository * @param array $classOrTraits * @param array $groups * @param array $paths - * @param Closure|null $beforeEach + * @param array $hooks */ - public function use(array $classOrTraits, array $groups, array $paths, ?Closure $beforeEach): void + public function use(array $classOrTraits, array $groups, array $paths, array $hooks): void { foreach ($classOrTraits as $classOrTrait) { if (!class_exists($classOrTrait) && !trait_exists($classOrTrait)) { @@ -111,10 +114,10 @@ final class TestRepository $this->uses[$path] = [ array_merge($this->uses[$path][0], $classOrTraits), array_merge($this->uses[$path][1], $groups), - $this->uses[$path][2] ?? $beforeEach, + $this->uses[$path][2] + $hooks, ]; } else { - $this->uses[$path] = [$classOrTraits, $groups, $beforeEach]; + $this->uses[$path] = [$classOrTraits, $groups, $hooks]; } } }