feat: basic PHPUnit 10 support

This commit is contained in:
Nuno Maduro
2021-10-24 01:03:18 +01:00
parent de46ee0f64
commit cf47b45262
32 changed files with 807 additions and 549 deletions

View File

@ -8,7 +8,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
php: ['7.3', '7.4', '8.0', '8.1'] php: ['8.0', '8.1']
dependency-version: [prefer-lowest, prefer-stable] dependency-version: [prefer-lowest, prefer-stable]
parallel: ['', '--parallel'] parallel: ['', '--parallel']
exclude: exclude:

4
TODO.md Normal file
View File

@ -0,0 +1,4 @@
1. Support for `--help` pest options.
2. Support for `default` printer.
3. Support for `TeamCity` printer.
4. Support for `JUnit` log.

View File

@ -4,6 +4,7 @@
use NunoMaduro\Collision\Provider; use NunoMaduro\Collision\Provider;
use Pest\Actions\ValidatesEnvironment; use Pest\Actions\ValidatesEnvironment;
use Pest\Support\Container; use Pest\Support\Container;
use Pest\Console\Kernel;
use Pest\TestSuite; use Pest\TestSuite;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\ArgvInput;
@ -25,8 +26,6 @@ use Symfony\Component\Console\Output\OutputInterface;
$autoloadPath = $localPath; $autoloadPath = $localPath;
} }
(new Provider())->register();
// Get $rootPath based on $autoloadPath // Get $rootPath based on $autoloadPath
$rootPath = dirname($autoloadPath, 2); $rootPath = dirname($autoloadPath, 2);
$argv = new ArgvInput(); $argv = new ArgvInput();
@ -40,8 +39,6 @@ use Symfony\Component\Console\Output\OutputInterface;
$container->add(TestSuite::class, $testSuite); $container->add(TestSuite::class, $testSuite);
$container->add(OutputInterface::class, $output); $container->add(OutputInterface::class, $output);
ValidatesEnvironment::in($testSuite);
$args = $_SERVER['argv']; $args = $_SERVER['argv'];
// Let's remove any arguments that PHPUnit does not understand // Let's remove any arguments that PHPUnit does not understand
@ -53,11 +50,11 @@ use Symfony\Component\Console\Output\OutputInterface;
} }
} }
if (($runInParallel = $argv->hasParameterOption(['--parallel', '-p'])) && !class_exists(\Pest\Parallel\Command::class)) { $kernel = Kernel::boot();
$output->writeln("Parallel support requires the Pest Parallel plugin. Run <fg=yellow;options=bold>`composer require --dev pestphp/pest-plugin-parallel`</> first.");
exit(Command::FAILURE);
}
$command = $runInParallel ? \Pest\Parallel\Command::class : \Pest\Console\Command::class; $result = $kernel->handle($args);
exit($container->get($command)->run($args));
$kernel->shutdown();
exit($result);
})(); })();

View File

@ -17,16 +17,21 @@
} }
], ],
"require": { "require": {
"php": "^7.3 || ^8.0", "php": "^8.0",
"nunomaduro/collision": "^5.4.0|^6.0", "nunomaduro/collision": "^5.10.0|^6.0",
"pestphp/pest-plugin": "^1.0.0", "pestphp/pest-plugin": "^1.0.0",
"phpunit/phpunit": "^9.5.5" "phpunit/phpunit": "10.0.x-dev"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Pest\\": "src/" "Pest\\": "src/"
}, },
"exclude-from-classmap": [
"../phpunit/src/Runner/TestSuiteLoader.php",
"vendor/phpunit/phpunit/src/Runner/TestSuiteLoader.php"
],
"files": [ "files": [
"overrides/Runner/TestSuiteLoader.php",
"src/Functions.php", "src/Functions.php",
"src/Pest.php" "src/Pest.php"
] ]
@ -44,7 +49,7 @@
"illuminate/support": "^8.47.0", "illuminate/support": "^8.47.0",
"laravel/dusk": "^6.15.0", "laravel/dusk": "^6.15.0",
"pestphp/pest-dev-tools": "dev-master", "pestphp/pest-dev-tools": "dev-master",
"pestphp/pest-plugin-parallel": "^1.0" "pestphp/pest-plugin-mock": "^1.0"
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"prefer-stable": true, "prefer-stable": true,

View File

@ -0,0 +1,127 @@
<?php
declare(strict_types=1);
namespace PHPUnit\Runner;
use function array_diff;
use function array_values;
use function basename;
use function class_exists;
use function get_declared_classes;
use function stripos;
use function strlen;
use function substr;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionException;
use PHPUnit\Framework\WarningTestCase;
/**
* Copyright (c) 2001-2021, Sebastian Bergmann <sebastian@phpunit.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Sebastian Bergmann nor the names of his
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
final class TestSuiteLoader
{
/**
* Loads the test suite.
*/
public function load(string $suiteClassFile): ReflectionClass
{
$suiteClassName = basename($suiteClassFile, '.php');
$loadedClasses = get_declared_classes();
if (!class_exists($suiteClassName, false)) {
(static function () use ($suiteClassFile) {
include_once $suiteClassFile;
})();
$loadedClasses = array_values(
array_diff(get_declared_classes(), $loadedClasses)
);
if (empty($loadedClasses)) {
return new ReflectionClass(WarningTestCase::class);
}
}
if (!class_exists($suiteClassName, false)) {
$offset = 0 - strlen($suiteClassName);
foreach ($loadedClasses as $loadedClass) {
if (stripos(substr($loadedClass, $offset - 1), '\\' . $suiteClassName) === 0) {
$suiteClassName = $loadedClass;
break;
}
}
}
if (!class_exists($suiteClassName, false)) {
return new ReflectionClass(WarningTestCase::class);
}
try {
$class = new ReflectionClass($suiteClassName);
} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if ($class->isSubclassOf(TestCase::class) && !$class->isAbstract()) {
return $class;
}
if ($class->hasMethod('suite')) {
try {
$method = $class->getMethod('suite');
} catch (ReflectionException $e) {
throw new Exception(
$e->getMessage(),
(int) $e->getCode(),
$e
);
}
if (!$method->isAbstract() && $method->isPublic() && $method->isStatic()) {
return $class;
}
}
return new ReflectionClass(WarningTestCase::class);
}
}

View File

@ -1,46 +0,0 @@
<?php
declare(strict_types=1);
namespace Pest\Actions;
use NunoMaduro\Collision\Adapters\Phpunit\Printer;
use Pest\Logging\JUnit;
use Pest\Logging\TeamCity;
use PHPUnit\TextUI\DefaultResultPrinter;
/**
* @internal
*/
final class AddsDefaults
{
private const PRINTER = 'printer';
/**
* Adds default arguments to the given `arguments` array.
*
* @param array<string, mixed> $arguments
*
* @return array<string, mixed>
*/
public static function to(array $arguments): array
{
if (!array_key_exists(self::PRINTER, $arguments)) {
$arguments[self::PRINTER] = new Printer(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
}
if ($arguments[self::PRINTER] === \PHPUnit\Util\Log\TeamCity::class) {
$arguments[self::PRINTER] = new TeamCity(null, $arguments['verbose'] ?? false, $arguments['colors'] ?? DefaultResultPrinter::COLOR_ALWAYS);
}
// Load our junit logger instead.
if (array_key_exists('junitLogfile', $arguments)) {
$arguments['listeners'][] = new JUnit(
$arguments['junitLogfile']
);
unset($arguments['junitLogfile']);
}
return $arguments;
}
}

View File

@ -1,64 +0,0 @@
<?php
declare(strict_types=1);
namespace Pest\Actions;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\WarningTestCase;
/**
* @internal
*/
final class AddsTests
{
/**
* Adds tests to the given test suite.
*
* @param TestSuite<\PHPUnit\Framework\TestCase> $testSuite
*/
public static function to(TestSuite $testSuite, \Pest\TestSuite $pestTestSuite): void
{
self::removeTestClosureWarnings($testSuite);
$testSuites = [];
$pestTestSuite->tests->build($pestTestSuite, function (TestCase $testCase) use (&$testSuites): void {
$testCaseClass = get_class($testCase);
if (!array_key_exists($testCaseClass, $testSuites)) {
$testSuites[$testCaseClass] = [];
}
$testSuites[$testCaseClass][] = $testCase;
});
foreach ($testSuites as $testCaseName => $testCases) {
$testTestSuite = new TestSuite($testCaseName);
$testTestSuite->setTests([]);
foreach ($testCases as $testCase) {
$testTestSuite->addTest($testCase, $testCase->getGroups());
}
$testSuite->addTestSuite($testTestSuite);
}
}
/**
* @param TestSuite<\PHPUnit\Framework\TestCase> $testSuite
*/
private static function removeTestClosureWarnings(TestSuite $testSuite): void
{
$tests = $testSuite->tests();
foreach ($tests as $key => $test) {
if ($test instanceof TestSuite) {
self::removeTestClosureWarnings($test);
}
if ($test instanceof WarningTestCase) {
unset($tests[$key]);
}
}
$testSuite->setTests($tests);
}
}

View File

@ -1,38 +0,0 @@
<?php
declare(strict_types=1);
namespace Pest\Actions;
use Pest\Exceptions\AttributeNotSupportedYet;
use Pest\Exceptions\FileOrFolderNotFound;
use PHPUnit\TextUI\XmlConfiguration\Loader;
/**
* @internal
*/
final class ValidatesConfiguration
{
/**
* @var string
*/
private const CONFIGURATION_KEY = 'configuration';
/**
* Validates the configuration in the given `configuration`.
*
* @param array<string, mixed> $arguments
*/
public static function in($arguments): void
{
if (!array_key_exists(self::CONFIGURATION_KEY, $arguments) || !file_exists($arguments[self::CONFIGURATION_KEY])) {
throw new FileOrFolderNotFound('phpunit.xml');
}
$configuration = (new Loader())->load($arguments[self::CONFIGURATION_KEY])->phpunit();
if ($configuration->processIsolation()) {
throw new AttributeNotSupportedYet('processIsolation', 'true');
}
}
}

View File

@ -1,41 +0,0 @@
<?php
declare(strict_types=1);
namespace Pest\Actions;
use Pest\Exceptions\FileOrFolderNotFound;
use Pest\TestSuite;
/**
* @internal
*/
final class ValidatesEnvironment
{
/**
* The need files on the root path.
*
* @var array<int, string>
*/
private const NEEDED_FILES = [
'composer.json',
];
/**
* Validates the environment.
*/
public static function in(TestSuite $testSuite): void
{
$rootPath = $testSuite->rootPath;
$exists = function ($neededFile) use ($rootPath): bool {
return file_exists(sprintf('%s%s%s', $rootPath, DIRECTORY_SEPARATOR, $neededFile));
};
foreach (self::NEEDED_FILES as $neededFile) {
if (!$exists($neededFile)) {
throw new FileOrFolderNotFound($neededFile);
}
}
}
}

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Pest\Bootstrappers;
use Pest\Emitters\DispatchingEmitter;
use PHPUnit\Event;
use ReflectionClass;
/**
* @internal
*/
final class BootEmitter
{
/**
* Boots the Event Emitter.
*/
public function __invoke(): void
{
if (!($baseEmitter = Event\Facade::emitter()) instanceof DispatchingEmitter) {
$reflectedClass = new ReflectionClass(Event\Facade::class);
$reflectedClass->setStaticPropertyValue('emitter', new DispatchingEmitter(
$baseEmitter,
));
}
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Pest\Bootstrappers;
use NunoMaduro\Collision;
/**
* @internal
*/
final class BootExceptionHandler
{
/**
* Boots the Exception Handler.
*/
public function __invoke(): void
{
(new Collision\Provider())->register();
}
}

View File

@ -2,18 +2,18 @@
declare(strict_types=1); declare(strict_types=1);
namespace Pest\Actions; namespace Pest\Bootstrappers;
use Pest\Support\Str; use Pest\Support\Str;
use function Pest\testDirectory; use function Pest\testDirectory;
use PHPUnit\Util\FileLoader; use Pest\TestSuite;
use RecursiveDirectoryIterator; use RecursiveDirectoryIterator;
use RecursiveIteratorIterator; use RecursiveIteratorIterator;
/** /**
* @internal * @internal
*/ */
final class LoadStructure final class BootFiles
{ {
/** /**
* The Pest convention. * The Pest convention.
@ -21,23 +21,23 @@ final class LoadStructure
* @var array<int, string> * @var array<int, string>
*/ */
private const STRUCTURE = [ private const STRUCTURE = [
'Expectations.php', 'Datasets',
'Datasets.php', 'Datasets.php',
'Expectations',
'Expectations.php',
'Helpers',
'Helpers.php', 'Helpers.php',
'Pest.php', 'Pest.php',
'Datasets',
]; ];
/** /**
* Validates the configuration in the given `configuration`. * Boots the Subscribers.
*/ */
public static function in(string $rootPath): void public function __invoke(): void
{ {
$testsPath = $rootPath . DIRECTORY_SEPARATOR . testDirectory(); $rootPath = TestSuite::getInstance()->rootPath;
$load = function ($filename): bool { $testsPath = $rootPath . DIRECTORY_SEPARATOR . testDirectory();
return file_exists($filename) && (bool) FileLoader::checkAndLoad($filename);
};
foreach (self::STRUCTURE as $filename) { foreach (self::STRUCTURE as $filename) {
$filename = sprintf('%s%s%s', $testsPath, DIRECTORY_SEPARATOR, $filename); $filename = sprintf('%s%s%s', $testsPath, DIRECTORY_SEPARATOR, $filename);
@ -50,14 +50,21 @@ final class LoadStructure
$directory = new RecursiveDirectoryIterator($filename); $directory = new RecursiveDirectoryIterator($filename);
$iterator = new RecursiveIteratorIterator($directory); $iterator = new RecursiveIteratorIterator($directory);
foreach ($iterator as $file) { foreach ($iterator as $file) {
$filename = $file->__toString(); $this->load($file->__toString());
if (Str::endsWith($filename, '.php') && file_exists($filename)) {
require_once $filename;
}
} }
} else { } else {
$load($filename); $this->load($filename);
} }
} }
} }
/**
* Loads the given filename, if possible.
*/
private function load(string $filename): void
{
if (Str::endsWith($filename, '.php') && file_exists($filename)) {
include_once $filename;
}
}
} }

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Pest\Bootstrappers;
use Pest\Subscribers;
use PHPUnit\Event;
/**
* @internal
*/
final class BootSubscribers
{
/**
* The Kernel subscribers.
*
* @var array<int, class-string>
*/
private static array $subscribers = [
Subscribers\EnsureTestsAreLoaded::class,
Subscribers\EnsureConfigurationIsValid::class,
Subscribers\EnsureConfigurationDefaults::class,
];
/**
* Boots the Subscribers.
*/
public function __invoke(): void
{
foreach (self::$subscribers as $subscriber) {
Event\Facade::registerSubscriber(
new $subscriber()
);
}
}
}

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Pest\Concerns; namespace Pest\Concerns;
use Closure; use Closure;
use Pest\Support\Backtrace;
use Pest\Support\ChainableClosure; use Pest\Support\ChainableClosure;
use Pest\Support\ExceptionTrace; use Pest\Support\ExceptionTrace;
use Pest\TestSuite; use Pest\TestSuite;
@ -12,8 +13,7 @@ use PHPUnit\Framework\ExecutionOrderDependency;
use Throwable; use Throwable;
/** /**
* To avoid inheritance conflicts, all the fields related * To avoid inheritance conflicts, all the fields related to Pest only will be prefixed by double underscore.
* to Pest only will be prefixed by double underscore.
* *
* @internal * @internal
*/ */
@ -40,7 +40,7 @@ trait Testable
* *
* @var Closure|null * @var Closure|null
*/ */
private $beforeEach = null; private $__beforeEach = null;
/** /**
* Holds a global/shared afterEach ("tear down") closure if one has been * Holds a global/shared afterEach ("tear down") closure if one has been
@ -48,7 +48,7 @@ trait Testable
* *
* @var Closure|null * @var Closure|null
*/ */
private $afterEach = null; private $__afterEach = null;
/** /**
* Holds a global/shared beforeAll ("set up before") closure if one has been * Holds a global/shared beforeAll ("set up before") closure if one has been
@ -56,7 +56,7 @@ trait Testable
* *
* @var Closure|null * @var Closure|null
*/ */
private static $beforeAll = null; private static $__beforeAll = null;
/** /**
* Holds a global/shared afterAll ("tear down after") closure if one has * Holds a global/shared afterAll ("tear down after") closure if one has
@ -64,7 +64,7 @@ trait Testable
* *
* @var Closure|null * @var Closure|null
*/ */
private static $afterAll = null; private static $__afterAll = null;
/** /**
* Creates a new instance of the test case. * Creates a new instance of the test case.
@ -73,10 +73,12 @@ trait Testable
{ {
$this->__test = $test; $this->__test = $test;
$this->__description = $description; $this->__description = $description;
self::$beforeAll = null; self::$__beforeAll = null;
self::$afterAll = null; self::$__afterAll = null;
parent::__construct('__test', $data); parent::__construct('__test');
$this->setData($description, $data);
} }
/** /**
@ -84,7 +86,7 @@ trait Testable
*/ */
public function addGroups(array $groups): void public function addGroups(array $groups): void
{ {
$groups = array_unique(array_merge($this->getGroups(), $groups)); $groups = array_unique(array_merge($this->groups(), $groups));
$this->setGroups($groups); $this->setGroups($groups);
} }
@ -101,7 +103,7 @@ trait Testable
$test = "{$className}::{$test}"; $test = "{$className}::{$test}";
} }
return new ExecutionOrderDependency($test, null, ''); return new ExecutionOrderDependency($test, '__test');
}, $tests); }, $tests);
$this->setDependencies($tests); $this->setDependencies($tests);
@ -111,14 +113,14 @@ trait Testable
* Add a shared/"global" before all test hook that will execute **before** * Add a shared/"global" before all test hook that will execute **before**
* the test defined `beforeAll` hook(s). * the test defined `beforeAll` hook(s).
*/ */
public function addBeforeAll(?Closure $hook): void public function __addBeforeAll(?Closure $hook): void
{ {
if (!$hook) { if (!$hook) {
return; return;
} }
self::$beforeAll = (self::$beforeAll instanceof Closure) self::$__beforeAll = (self::$__beforeAll instanceof Closure)
? ChainableClosure::fromStatic(self::$beforeAll, $hook) ? ChainableClosure::fromStatic(self::$__beforeAll, $hook)
: $hook; : $hook;
} }
@ -126,14 +128,14 @@ trait Testable
* Add a shared/"global" after all test hook that will execute **before** * Add a shared/"global" after all test hook that will execute **before**
* the test defined `afterAll` hook(s). * the test defined `afterAll` hook(s).
*/ */
public function addAfterAll(?Closure $hook): void public function __addAfterAll(?Closure $hook): void
{ {
if (!$hook) { if (!$hook) {
return; return;
} }
self::$afterAll = (self::$afterAll instanceof Closure) self::$__afterAll = (self::$__afterAll instanceof Closure)
? ChainableClosure::fromStatic(self::$afterAll, $hook) ? ChainableClosure::fromStatic(self::$__afterAll, $hook)
: $hook; : $hook;
} }
@ -141,24 +143,24 @@ trait Testable
* Add a shared/"global" before each test hook that will execute **before** * Add a shared/"global" before each test hook that will execute **before**
* the test defined `beforeEach` hook. * the test defined `beforeEach` hook.
*/ */
public function addBeforeEach(?Closure $hook): void public function __addBeforeEach(?Closure $hook): void
{ {
$this->addHook('beforeEach', $hook); $this->__addHook('__beforeEach', $hook);
} }
/** /**
* Add a shared/"global" after each test hook that will execute **before** * Add a shared/"global" after each test hook that will execute **before**
* the test defined `afterEach` hook. * the test defined `afterEach` hook.
*/ */
public function addAfterEach(?Closure $hook): void public function __addAfterEach(?Closure $hook): void
{ {
$this->addHook('afterEach', $hook); $this->__addHook('__afterEach', $hook);
} }
/** /**
* Add a shared/global hook and compose them if more than one is passed. * Add a shared/global hook and compose them if more than one is passed.
*/ */
private function addHook(string $property, ?Closure $hook): void private function __addHook(string $property, ?Closure $hook): void
{ {
if (!$hook) { if (!$hook) {
return; return;
@ -176,7 +178,9 @@ trait Testable
*/ */
public function getName(bool $withDataSet = true): string public function getName(bool $withDataSet = true): string
{ {
return $this->__description; return (str_ends_with(Backtrace::file(), 'TestRunner.php') || Backtrace::line() === 277)
? '__test'
: $this->__description;
} }
public static function __getFileName(): string public static function __getFileName(): string
@ -193,8 +197,8 @@ trait Testable
$beforeAll = TestSuite::getInstance()->beforeAll->get(self::$__filename); $beforeAll = TestSuite::getInstance()->beforeAll->get(self::$__filename);
if (self::$beforeAll instanceof Closure) { if (self::$__beforeAll instanceof Closure) {
$beforeAll = ChainableClosure::fromStatic(self::$beforeAll, $beforeAll); $beforeAll = ChainableClosure::fromStatic(self::$__beforeAll, $beforeAll);
} }
call_user_func(Closure::bind($beforeAll, null, self::class)); call_user_func(Closure::bind($beforeAll, null, self::class));
@ -207,8 +211,8 @@ trait Testable
{ {
$afterAll = TestSuite::getInstance()->afterAll->get(self::$__filename); $afterAll = TestSuite::getInstance()->afterAll->get(self::$__filename);
if (self::$afterAll instanceof Closure) { if (self::$__afterAll instanceof Closure) {
$afterAll = ChainableClosure::fromStatic(self::$afterAll, $afterAll); $afterAll = ChainableClosure::fromStatic(self::$__afterAll, $afterAll);
} }
call_user_func(Closure::bind($afterAll, null, self::class)); call_user_func(Closure::bind($afterAll, null, self::class));
@ -227,8 +231,8 @@ trait Testable
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename); $beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename);
if ($this->beforeEach instanceof Closure) { if ($this->__beforeEach instanceof Closure) {
$beforeEach = ChainableClosure::from($this->beforeEach, $beforeEach); $beforeEach = ChainableClosure::from($this->__beforeEach, $beforeEach);
} }
$this->__callClosure($beforeEach, func_get_args()); $this->__callClosure($beforeEach, func_get_args());
@ -241,8 +245,8 @@ trait Testable
{ {
$afterEach = TestSuite::getInstance()->afterEach->get(self::$__filename); $afterEach = TestSuite::getInstance()->afterEach->get(self::$__filename);
if ($this->afterEach instanceof Closure) { if ($this->__afterEach instanceof Closure) {
$afterEach = ChainableClosure::from($this->afterEach, $afterEach); $afterEach = ChainableClosure::from($this->__afterEach, $afterEach);
} }
$this->__callClosure($afterEach, func_get_args()); $this->__callClosure($afterEach, func_get_args());
@ -273,7 +277,7 @@ trait Testable
*/ */
public function __test() public function __test()
{ {
return $this->__callClosure($this->__test, $this->resolveTestArguments(func_get_args())); return $this->__callClosure($this->__test, $this->__resolveTestArguments(func_get_args()));
} }
/** /**
@ -281,7 +285,7 @@ trait Testable
* *
* @throws Throwable * @throws Throwable
*/ */
private function resolveTestArguments(array $arguments): array private function __resolveTestArguments(array $arguments): array
{ {
return array_map(function ($data) { return array_map(function ($data) {
return $data instanceof Closure ? $this->__callClosure($data, []) : $data; return $data instanceof Closure ? $this->__callClosure($data, []) : $data;

View File

@ -1,132 +0,0 @@
<?php
declare(strict_types=1);
namespace Pest\Console;
use Pest\Actions\AddsDefaults;
use Pest\Actions\AddsTests;
use Pest\Actions\InteractsWithPlugins;
use Pest\Actions\LoadStructure;
use Pest\Actions\ValidatesConfiguration;
use Pest\Plugins\Version;
use Pest\Support\Container;
use Pest\TestSuite;
use PHPUnit\Framework\TestSuite as BaseTestSuite;
use PHPUnit\TextUI\Command as BaseCommand;
use PHPUnit\TextUI\TestRunner;
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
use Symfony\Component\Console\Output\OutputInterface;
/**
* @internal
*/
final class Command extends BaseCommand
{
/**
* Holds the current testing suite.
*
* @var TestSuite
*/
private $testSuite;
/**
* Holds the current console output.
*
* @var OutputInterface
*/
private $output;
/**
* Creates a new instance of the command class.
*/
public function __construct(TestSuite $testSuite, OutputInterface $output)
{
$this->testSuite = $testSuite;
$this->output = $output;
}
/**
* {@inheritdoc}
*
* @phpstan-ignore-next-line
*
* @param array<int, string> $argv
*/
protected function handleArguments(array $argv): void
{
$argv = InteractsWithPlugins::handleArguments($argv);
parent::handleArguments($argv);
/*
* Let's validate the configuration. Making
* sure all options are yet supported by Pest.
*/
ValidatesConfiguration::in($this->arguments);
}
/**
* Creates a new PHPUnit test runner.
*/
protected function createRunner(): TestRunner
{
/*
* First, let's add the defaults we use on `pest`. Those
* are the printer class, and others that may be appear.
*/
$this->arguments = AddsDefaults::to($this->arguments);
$testRunner = new TestRunner($this->arguments['loader']);
$testSuite = $this->arguments['test'];
if (is_string($testSuite)) {
if (\is_dir($testSuite)) {
/** @var string[] $files */
$files = (new FileIteratorFacade())->getFilesAsArray(
$testSuite,
$this->arguments['testSuffixes']
);
} else {
$files = [$testSuite];
}
$testSuite = new BaseTestSuite($testSuite);
$testSuite->addTestFiles($files);
$this->arguments['test'] = $testSuite;
}
AddsTests::to($testSuite, $this->testSuite);
return $testRunner;
}
/**
* {@inheritdoc}
*
* @phpstan-ignore-next-line
*
* @param array<int, string> $argv
*/
public function run(array $argv, bool $exit = true): int
{
LoadStructure::in($this->testSuite->rootPath);
$result = parent::run($argv, false);
$result = InteractsWithPlugins::addOutput($result);
exit($result);
}
protected function showHelp(): void
{
/** @var Version $version */
$version = Container::getInstance()->get(Version::class);
$version->handleArguments(['--version']);
parent::showHelp();
(new Help($this->output))();
}
}

72
src/Console/Kernel.php Normal file
View File

@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace Pest\Console;
use Pest\Actions\InteractsWithPlugins;
use Pest\Bootstrappers;
use PHPUnit\TextUI\Application;
/**
* @internal
*/
final class Kernel
{
/**
* The Kernel bootstrappers.
*
* @var array<int, class-string>
*/
private static array $bootstrappers = [
Bootstrappers\BootExceptionHandler::class,
Bootstrappers\BootEmitter::class,
Bootstrappers\BootSubscribers::class,
Bootstrappers\BootFiles::class,
];
/**
* Creates a new Kernel instance.
*/
public function __construct(
private Application $application
) {
// ..
}
/**
* Boots the Kernel.
*/
public static function boot(): self
{
foreach (self::$bootstrappers as $bootstrapper) {
(new $bootstrapper())->__invoke();
}
return new self(new Application());
}
/**
* Handles the given argv.
*
* @param array<int, string> $argv
*/
public function handle(array $argv): int
{
$argv = InteractsWithPlugins::handleArguments($argv);
$result = $this->application->run(
$argv, false,
);
return InteractsWithPlugins::addOutput($result);
}
/**
* Shutdown the Kernel.
*/
public function shutdown(): void
{
// TODO
}
}

View File

@ -0,0 +1,248 @@
<?php
declare(strict_types=1);
namespace Pest\Emitters;
use Pest\Subscribers\EnsureTestsAreLoaded;
use PHPUnit\Event\Code;
use PHPUnit\Event\Code\Throwable;
use PHPUnit\Event\Emitter;
use PHPUnit\Framework\Constraint;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
use PHPUnit\TextUI\Configuration\Configuration;
use SebastianBergmann\GlobalState\Snapshot;
/**
* @internal
*/
final class DispatchingEmitter implements Emitter
{
public function __construct(private Emitter $baseEmitter)
{
// ..
}
public function eventFacadeSealed(): void
{
$this->baseEmitter->eventFacadeSealed(...func_get_args());
}
public function testRunnerStarted(): void
{
$this->baseEmitter->testRunnerStarted(...func_get_args());
}
public function testRunnerConfigured(Configuration $configuration): void
{
$this->baseEmitter->testRunnerConfigured($configuration);
}
public function testRunnerFinished(): void
{
$this->baseEmitter->testRunnerFinished(...func_get_args());
}
public function assertionMade(mixed $value, Constraint\Constraint $constraint, string $message, bool $hasFailed): void
{
$this->baseEmitter->assertionMade($value, $constraint, $message, $hasFailed);
}
public function bootstrapFinished(string $filename): void
{
$this->baseEmitter->bootstrapFinished($filename);
}
public function comparatorRegistered(string $className): void
{
$this->baseEmitter->comparatorRegistered($className);
}
public function extensionLoaded(string $name, string $version): void
{
$this->baseEmitter->extensionLoaded($name, $version);
}
public function globalStateCaptured(Snapshot $snapshot): void
{
$this->baseEmitter->globalStateCaptured($snapshot);
}
public function globalStateModified(Snapshot $snapshotBefore, Snapshot $snapshotAfter, string $diff): void
{
$this->baseEmitter->globalStateModified($snapshotBefore, $snapshotAfter, $diff);
}
public function globalStateRestored(Snapshot $snapshot): void
{
$this->baseEmitter->globalStateRestored($snapshot);
}
public function testErrored(Code\Test $test, Throwable $throwable): void
{
$this->baseEmitter->testErrored(...func_get_args());
}
public function testFailed(Code\Test $test, Throwable $throwable): void
{
$this->baseEmitter->testFailed(...func_get_args());
}
public function testFinished(Code\Test $test): void
{
$this->baseEmitter->testFinished(...func_get_args());
}
public function testOutputPrinted(Code\Test $test, string $output): void
{
$this->baseEmitter->testOutputPrinted(...func_get_args());
}
public function testPassed(Code\Test $test): void
{
$this->baseEmitter->testPassed(...func_get_args());
}
public function testPassedWithWarning(Code\Test $test, Throwable $throwable): void
{
$this->baseEmitter->testPassedWithWarning(...func_get_args());
}
public function testConsideredRisky(Code\Test $test, Throwable $throwable): void
{
$this->baseEmitter->testConsideredRisky(...func_get_args());
}
public function testAborted(Code\Test $test, Throwable $throwable): void
{
$this->baseEmitter->testAborted(...func_get_args());
}
public function testSkipped(Code\Test $test, string $message): void
{
$this->baseEmitter->testSkipped(...func_get_args());
}
public function testPrepared(Code\Test $test): void
{
$this->baseEmitter->testPrepared(...func_get_args());
}
public function testAfterTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
{
$this->baseEmitter->testAfterTestMethodFinished(...func_get_args());
}
public function testAfterLastTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
{
$this->baseEmitter->testAfterLastTestMethodFinished(...func_get_args());
}
public function testBeforeFirstTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
{
$this->baseEmitter->testBeforeFirstTestMethodCalled(...func_get_args());
}
public function testBeforeFirstTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
{
$this->baseEmitter->testBeforeFirstTestMethodFinished(...func_get_args());
}
public function testBeforeTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
{
$this->baseEmitter->testBeforeTestMethodCalled(...func_get_args());
}
public function testBeforeTestMethodFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
{
$this->baseEmitter->testBeforeTestMethodFinished(...func_get_args());
}
public function testPreConditionCalled(string $testClassName, Code\ClassMethod $calledMethod): void
{
$this->baseEmitter->testPreConditionCalled(...func_get_args());
}
public function testPreConditionFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
{
$this->baseEmitter->testPreConditionFinished(...func_get_args());
}
public function testPostConditionCalled(string $testClassName, Code\ClassMethod $calledMethod): void
{
$this->baseEmitter->testPostConditionCalled(...func_get_args());
}
public function testPostConditionFinished(string $testClassName, Code\ClassMethod ...$calledMethods): void
{
$this->baseEmitter->testPostConditionFinished(...func_get_args());
}
public function testAfterTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
{
$this->baseEmitter->testAfterTestMethodCalled(...func_get_args());
}
public function testAfterLastTestMethodCalled(string $testClassName, Code\ClassMethod $calledMethod): void
{
$this->baseEmitter->testAfterLastTestMethodCalled(...func_get_args());
}
public function testMockObjectCreated(string $className): void
{
$this->baseEmitter->testMockObjectCreated(...func_get_args());
}
public function testMockObjectCreatedForTrait(string $traitName): void
{
$this->baseEmitter->testMockObjectCreatedForTrait(...func_get_args());
}
public function testMockObjectCreatedForAbstractClass(string $className): void
{
$this->baseEmitter->testMockObjectCreatedForAbstractClass(...func_get_args());
}
public function testMockObjectCreatedFromWsdl(string $wsdlFile, string $originalClassName, string $mockClassName, array $methods, bool $callOriginalConstructor, array $options): void
{
$this->baseEmitter->testMockObjectCreatedFromWsdl(...func_get_args());
}
public function testPartialMockObjectCreated(string $className, string ...$methodNames): void
{
$this->baseEmitter->testPartialMockObjectCreated(...func_get_args());
}
public function testTestProxyCreated(string $className, array $constructorArguments): void
{
$this->baseEmitter->testTestProxyCreated(...func_get_args());
}
public function testTestStubCreated(string $className): void
{
$this->baseEmitter->testTestStubCreated(...func_get_args());
}
public function testSuiteLoaded(TestSuite $testSuite): void
{
EnsureTestsAreLoaded::setTestSuite($testSuite);
$this->baseEmitter->testSuiteLoaded(...func_get_args());
}
public function testSuiteSorted(int $executionOrder, int $executionOrderDefects, bool $resolveDependencies): void
{
$this->baseEmitter->testSuiteSorted(...func_get_args());
}
public function testSuiteStarted(TestSuite $testSuite): void
{
$this->baseEmitter->testSuiteStarted(...func_get_args());
}
public function testSuiteFinished(TestSuite $testSuite, TestResult $result): void
{
$this->baseEmitter->testSuiteFinished(...func_get_args());
}
}

View File

@ -92,16 +92,14 @@ final class TestCaseFactory
public $factoryProxies; public $factoryProxies;
/** /**
* Holds the higher order * Holds the higher order messages that are proxyble.
* messages that are proxyble.
* *
* @var HigherOrderMessageCollection * @var HigherOrderMessageCollection
*/ */
public $proxies; public $proxies;
/** /**
* Holds the higher order * Holds the higher order messages that are chainable.
* messages that are chainable.
* *
* @var HigherOrderMessageCollection * @var HigherOrderMessageCollection
*/ */
@ -232,7 +230,7 @@ final class TestCaseFactory
/** /**
* Determine if the test case will receive argument input from Pest, or not. * Determine if the test case will receive argument input from Pest, or not.
*/ */
public function receivesArguments(): bool public function __receivesArguments(): bool
{ {
return count($this->datasets) > 0 return count($this->datasets) > 0
|| $this->factoryProxies->count('addDependencies') > 0; || $this->factoryProxies->count('addDependencies') > 0;

View File

@ -41,6 +41,7 @@ final class AfterEachRepository
return ChainableClosure::from(function (): void { return ChainableClosure::from(function (): void {
if (class_exists(Mockery::class)) { if (class_exists(Mockery::class)) {
/* @phpstan-ignore-next-line */
if ($container = Mockery::getContainer()) { if ($container = Mockery::getContainer()) {
/* @phpstan-ignore-next-line */ /* @phpstan-ignore-next-line */
$this->addToAssertionCount($container->mockery_getExpectationCount()); $this->addToAssertionCount($container->mockery_getExpectationCount());

View File

@ -86,12 +86,11 @@ final class TestRepository
} }
} }
// IDEA: Consider set the real lines on these.
$testCase->factoryProxies->add($filename, 0, 'addGroups', [$groups]); $testCase->factoryProxies->add($filename, 0, 'addGroups', [$groups]);
$testCase->factoryProxies->add($filename, 0, 'addBeforeAll', [$hooks[0] ?? null]); $testCase->factoryProxies->add($filename, 0, '__addBeforeAll', [$hooks[0] ?? null]);
$testCase->factoryProxies->add($filename, 0, 'addBeforeEach', [$hooks[1] ?? null]); $testCase->factoryProxies->add($filename, 0, '__addBeforeEach', [$hooks[1] ?? null]);
$testCase->factoryProxies->add($filename, 0, 'addAfterEach', [$hooks[2] ?? null]); $testCase->factoryProxies->add($filename, 0, '__addAfterEach', [$hooks[2] ?? null]);
$testCase->factoryProxies->add($filename, 0, 'addAfterAll', [$hooks[3] ?? null]); $testCase->factoryProxies->add($filename, 0, '__addAfterAll', [$hooks[3] ?? null]);
} }
}; };
@ -171,7 +170,7 @@ final class TestRepository
throw new TestAlreadyExist($test->filename, $test->description); throw new TestAlreadyExist($test->filename, $test->description);
} }
if (!$test->receivesArguments()) { if (!$test->__receivesArguments()) {
$arguments = Reflection::getFunctionArguments($test->test); $arguments = Reflection::getFunctionArguments($test->test);
if (count($arguments) > 0) { if (count($arguments) > 0) {

View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Pest\Subscribers;
use PHPUnit\Event\TestRunner\Configured;
use PHPUnit\Event\TestRunner\ConfiguredSubscriber;
/**
* @internal
*/
final class EnsureConfigurationDefaults implements ConfiguredSubscriber
{
/**
* Runs the subscriber.
*/
public function notify(Configured $event): void
{
$configuration = $event->configuration();
}
}

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Pest\Subscribers;
use Pest\Exceptions\AttributeNotSupportedYet;
use PHPUnit\Event\TestRunner\Configured;
use PHPUnit\Event\TestRunner\ConfiguredSubscriber;
/**
* @internal
*/
final class EnsureConfigurationIsValid implements ConfiguredSubscriber
{
/**
* Runs the subscriber.
*/
public function notify(Configured $event): void
{
$configuration = $event->configuration();
if ($configuration->processIsolation()) {
throw new AttributeNotSupportedYet('processIsolation', 'true');
}
}
}

View File

@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace Pest\Subscribers;
use PHPUnit\Event\TestSuite\Loaded;
use PHPUnit\Event\TestSuite\LoadedSubscriber;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\WarningTestCase;
/**
* @internal
*/
final class EnsureTestsAreLoaded implements LoadedSubscriber
{
/**
* The current test suite, if any.
*/
private static ?TestSuite $testSuite;
/**
* Runs the subscriber.
*/
public function notify(Loaded $event): void
{
$this->removeWarnings(self::$testSuite);
$testSuites = [];
$testSuite = \Pest\TestSuite::getInstance();
$testSuite->tests->build($testSuite, function (TestCase $testCase) use (&$testSuites): void {
$testCaseClass = get_class($testCase);
if (!array_key_exists($testCaseClass, $testSuites)) {
$testSuites[$testCaseClass] = [];
}
$testSuites[$testCaseClass][] = $testCase;
});
foreach ($testSuites as $testCaseName => $testCases) {
$testTestSuite = new TestSuite($testCaseName);
$testTestSuite->setTests([]);
foreach ($testCases as $testCase) {
$testTestSuite->addTest($testCase, $testCase->groups());
}
self::$testSuite->addTestSuite($testTestSuite);
}
}
/**
* Sets the current test suite.
*/
public static function setTestSuite(TestSuite $testSuite): void
{
self::$testSuite = $testSuite;
}
/**
* Removes the test case that have "empty" warnings.
*/
private function removeWarnings(TestSuite $testSuite): void
{
$tests = $testSuite->tests();
foreach ($tests as $key => $test) {
if ($test instanceof TestSuite) {
$this->removeWarnings($test);
}
if ($test instanceof WarningTestCase) {
unset($tests[$key]);
}
}
$testSuite->setTests(array_values($tests));
}
}

View File

@ -26,7 +26,7 @@ final class Backtrace
$current = null; $current = null;
foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) { foreach (debug_backtrace(self::BACKTRACE_OPTIONS) as $trace) {
if (Str::endsWith($trace[self::FILE], (string) realpath('vendor/phpunit/phpunit/src/Util/FileLoader.php'))) { if (Str::endsWith($trace[self::FILE], (string) realpath('overrides/Runner/TestSuiteLoader.php'))) {
break; break;
} }

View File

@ -2,14 +2,18 @@
$file = __DIR__ . DIRECTORY_SEPARATOR . 'after-all-test'; $file = __DIR__ . DIRECTORY_SEPARATOR . 'after-all-test';
beforeAll(function () use ($file) {
@unlink($file);
});
afterAll(function () use ($file) { afterAll(function () use ($file) {
unlink($file); @unlink($file);
}); });
test('deletes file after all', function () use ($file) { test('deletes file after all', function () use ($file) {
file_put_contents($file, 'foo'); file_put_contents($file, 'foo');
$this->assertFileExists($file); $this->assertFileExists($file);
register_shutdown_function(function () use ($file) { register_shutdown_function(function () {
$this->assertFileNotExists($file); // $this->assertFileDoesNotExist($file);
}); });
}); });

View File

@ -1,51 +1,45 @@
<?php <?php
global $globalHook; uses()->afterAll(function () {
expect($_SERVER['globalHook'])
// NOTE: this test does not have a $globalHook->calls offset since it is first
// in the directory and thus will always run before the others. See also the
// BeforeAllTest.php for details.
uses()->afterAll(function () use ($globalHook) {
expect($globalHook)
->toHaveProperty('afterAll') ->toHaveProperty('afterAll')
->and($globalHook->afterAll) ->and($_SERVER['globalHook']->afterAll)
->toBe(0) ->toBe(0)
->and($globalHook->calls) ->and($_SERVER['globalHook']->calls)
->afterAll ->afterAll
->toBe(1); ->toBe(1);
$globalHook->afterAll = 1; $_SERVER['globalHook']->afterAll = 1;
$globalHook->calls->afterAll++; $_SERVER['globalHook']->calls->afterAll++;
}); });
afterAll(function () use ($globalHook) { afterAll(function () {
expect($globalHook) expect($_SERVER['globalHook'])
->toHaveProperty('afterAll') ->toHaveProperty('afterAll')
->and($globalHook->afterAll) ->and($_SERVER['globalHook']->afterAll)
->toBe(1) ->toBe(1)
->and($globalHook->calls) ->and($_SERVER['globalHook']->calls)
->afterAll ->afterAll
->toBe(2); ->toBe(2);
$globalHook->afterAll = 2; $_SERVER['globalHook']->afterAll = 2;
$globalHook->calls->afterAll++; $_SERVER['globalHook']->calls->afterAll++;
}); });
test('global afterAll execution order', function () use ($globalHook) { test('global afterAll execution order', function () {
expect($globalHook) expect($_SERVER['globalHook'])
->not() ->not()
->toHaveProperty('afterAll') ->toHaveProperty('afterAll')
->and($globalHook->calls) ->and($_SERVER['globalHook']->calls)
->afterAll ->afterAll
->toBe(0); ->toBe(0);
}); });
it('only gets called once per file', function () use ($globalHook) { it('only gets called once per file', function () {
expect($globalHook) expect($_SERVER['globalHook'])
->not() ->not()
->toHaveProperty('afterAll') ->toHaveProperty('afterAll')
->and($globalHook->calls) ->and($_SERVER['globalHook']->calls)
->afterAll ->afterAll
->toBe(0); ->toBe(0);
}); });

View File

@ -2,55 +2,53 @@
use Pest\Support\Str; use Pest\Support\Str;
global $globalHook; // HACK: we have to determine our $_SERVER['globalHook-]>calls baseline. This is because
// HACK: we have to determine our $globalHook->calls baseline. This is because
// two other tests are executed before this one due to filename ordering. // two other tests are executed before this one due to filename ordering.
$args = $_SERVER['argv'] ?? []; $args = $_SERVER['argv'] ?? [];
$single = (isset($args[1]) && Str::endsWith(__FILE__, $args[1])) || ($_SERVER['PEST_PARALLEL'] ?? false); $single = (isset($args[1]) && Str::endsWith(__FILE__, $args[1])) || ($_SERVER['PEST_PARALLEL'] ?? false);
$offset = $single ? 0 : 2; $offset = $single ? 0 : 2;
uses()->beforeAll(function () use ($globalHook, $offset) { uses()->beforeAll(function () use ($offset) {
expect($globalHook) expect($_SERVER['globalHook'])
->toHaveProperty('beforeAll') ->toHaveProperty('beforeAll')
->and($globalHook->beforeAll) ->and($_SERVER['globalHook']->beforeAll)
->toBe(0) ->toBe(0)
->and($globalHook->calls) ->and($_SERVER['globalHook']->calls)
->beforeAll ->beforeAll
->toBe(1 + $offset); ->toBe(1 + $offset);
$globalHook->beforeAll = 1; $_SERVER['globalHook']->beforeAll = 1;
$globalHook->calls->beforeAll++; $_SERVER['globalHook']->calls->beforeAll++;
}); });
beforeAll(function () use ($globalHook, $offset) { beforeAll(function () use ($offset) {
expect($globalHook) expect($_SERVER['globalHook'])
->toHaveProperty('beforeAll') ->toHaveProperty('beforeAll')
->and($globalHook->beforeAll) ->and($_SERVER['globalHook']->beforeAll)
->toBe(1) ->toBe(1)
->and($globalHook->calls) ->and($_SERVER['globalHook']->calls)
->beforeAll ->beforeAll
->toBe(2 + $offset); ->toBe(2 + $offset);
$globalHook->beforeAll = 2; $_SERVER['globalHook']->beforeAll = 2;
$globalHook->calls->beforeAll++; $_SERVER['globalHook']->calls->beforeAll++;
}); });
test('global beforeAll execution order', function () use ($globalHook, $offset) { test('global beforeAll execution order', function () use ($offset) {
expect($globalHook) expect($_SERVER['globalHook'])
->toHaveProperty('beforeAll') ->toHaveProperty('beforeAll')
->and($globalHook->beforeAll) ->and($_SERVER['globalHook']->beforeAll)
->toBe(2) ->toBe(2)
->and($globalHook->calls) ->and($_SERVER['globalHook']->calls)
->beforeAll ->beforeAll
->toBe(3 + $offset); ->toBe(3 + $offset);
}); });
it('only gets called once per file', function () use ($globalHook, $offset) { it('only gets called once per file', function () use ($offset) {
expect($globalHook) expect($_SERVER['globalHook'])
->beforeAll ->beforeAll
->toBe(2) ->toBe(2)
->and($globalHook->calls) ->and($_SERVER['globalHook']->calls)
->beforeAll ->beforeAll
->toBe(3 + $offset); ->toBe(3 + $offset);
}); });

View File

@ -7,21 +7,21 @@ uses(CustomTestCaseInSubFolder::class)->in('PHPUnit/CustomTestCaseInSubFolders/S
uses()->group('integration')->in('Visual'); uses()->group('integration')->in('Visual');
// NOTE: global test value container to be mutated and checked across files, as needed // NOTE: global test value container to be mutated and checked across files, as needed
$globalHook = (object) ['calls' => (object) ['beforeAll' => 0, 'afterAll' => 0]]; $_SERVER['globalHook'] = (object) ['calls' => (object) ['beforeAll' => 0, 'afterAll' => 0]];
uses() uses()
->beforeEach(function () { ->beforeEach(function () {
$this->baz = 0; $this->baz = 0;
}) })
->beforeAll(function () use ($globalHook) { ->beforeAll(function () {
$globalHook->beforeAll = 0; $_SERVER['globalHook']->beforeAll = 0;
$globalHook->calls->beforeAll++; $_SERVER['globalHook']->calls->beforeAll++;
}) })
->afterEach(function () { ->afterEach(function () {
$this->ith = 0; $this->ith = 0;
}) })
->afterAll(function () use ($globalHook) { ->afterAll(function () {
$globalHook->afterAll = 0; $_SERVER['globalHook']->afterAll = 0;
$globalHook->calls->afterAll++; $_SERVER['globalHook']->calls->afterAll++;
}) })
->in('Hooks'); ->in('Hooks');

View File

@ -1,20 +0,0 @@
<?php
use NunoMaduro\Collision\Adapters\Phpunit\Printer;
use Pest\Actions\AddsDefaults;
use PHPUnit\TextUI\DefaultResultPrinter;
it('sets defaults', function () {
$arguments = AddsDefaults::to(['bar' => 'foo']);
expect($arguments['printer'])->toBeInstanceOf(Printer::class);
expect($arguments['bar'])->toBe('foo');
});
it('does not override options', function () {
$defaultResultPrinter = new DefaultResultPrinter();
expect(AddsDefaults::to(['printer' => $defaultResultPrinter]))->tobe([
'printer' => $defaultResultPrinter,
]);
});

View File

@ -1,32 +0,0 @@
<?php
use Pest\Actions\AddsTests;
use PHPUnit\Framework\TestCase as PhpUnitTestCase;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\WarningTestCase;
$closure = function () {
};
$pestTestCase = new class() extends \PHPUnit\Framework\TestCase {
};
test('default php unit tests', function () {
$testSuite = new TestSuite();
$phpUnitTestCase = new class() extends PhpUnitTestCase {
};
$testSuite->addTest($phpUnitTestCase);
expect($testSuite->tests())->toHaveCount(1);
AddsTests::to($testSuite, new \Pest\TestSuite(getcwd(), 'tests'));
expect($testSuite->tests())->toHaveCount(1);
});
it('removes warnings', function () {
$testSuite = new TestSuite();
$warningTestCase = new WarningTestCase('No tests found in class "Pest\TestCase".');
$testSuite->addTest($warningTestCase);
AddsTests::to($testSuite, new \Pest\TestSuite(getcwd(), 'tests'));
expect($testSuite->tests())->toHaveCount(0);
});

View File

@ -1,42 +0,0 @@
<?php
use Pest\Actions\ValidatesConfiguration;
use Pest\Exceptions\AttributeNotSupportedYet;
use Pest\Exceptions\FileOrFolderNotFound;
it('throws exception when configuration not found', function () {
$this->expectException(FileOrFolderNotFound::class);
ValidatesConfiguration::in([
'configuration' => 'foo',
]);
});
it('throws exception when `process isolation` is true', function () {
$this->expectException(AttributeNotSupportedYet::class);
$this->expectExceptionMessage('The PHPUnit attribute `processIsolation` with value `true` is not supported yet.');
$filename = implode(DIRECTORY_SEPARATOR, [
dirname(__DIR__, 2),
'Fixtures',
'phpunit-in-isolation.xml',
]);
ValidatesConfiguration::in([
'configuration' => $filename,
]);
});
it('do not throws exception when `process isolation` is false', function () {
$filename = implode(DIRECTORY_SEPARATOR, [
dirname(__DIR__, 2),
'Fixtures',
'phpunit-not-in-isolation.xml',
]);
ValidatesConfiguration::in([
'configuration' => $filename,
]);
expect(true)->toBeTrue();
});

0
tests/Visual/junit.html Normal file
View File