fix: usage of named arguments

This commit is contained in:
Nuno Maduro
2024-02-01 13:45:06 +00:00
parent 3dffdf7cb8
commit 7b9bae0415
10 changed files with 213 additions and 70 deletions

View File

@ -18,13 +18,13 @@ final class BootOverrides implements Bootstrapper
* @var array<string, string>
*/
public const FILES = [
'c7b9c8a96006dea314204a8f09a8764e51ce0b9b79aadd58da52e8c328db4870' => 'Runner/Filter/NameFilterIterator.php',
'ec723a9efae521dd6576d2e7d746cc12d3e27f271c49c46420fba8a0e161a61f' => 'Runner/Filter/NameFilterIterator.php',
'52b2574e96269aca1bb2d41bbf418c3bcf23dd21d14c66f90789025c309e39df' => 'Runner/ResultCache/DefaultResultCache.php',
'bc8718c89264f65800beabc23e51c6d3bcff87dfc764a12179ef5dbfde272c8b' => 'Runner/TestSuiteLoader.php',
'f41e48d6cb546772a7de4f8e66b6b7ce894a5318d063eb52e354d206e96c701c' => 'TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php',
'cb7519f2d82893640b694492cf7ec9528da80773cc1d259634181b5d393528b5' => 'TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php',
'6db25ee539e9b12b1fb4e044a0a93410e015bc983ecdd3909cd394fe44ae8c95' => 'TextUI/TestSuiteFilterProcessor.php',
'ef64a657ed9c0067791483784944107827bf227c7e3200f212b6751876b99e25' => 'Event/Value/ThrowableBuilder.php',
'2ef8e21dbb27cf6597dd9bb0f941c063dcc98b5af2c35d10b1c2d77721582e8f' => 'TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php',
'badc88c79c2a47d768be3925051999b158d08b64e57ccf4ce560f1610cbcc1e8' => 'TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php',
'8607a62825a762735721c39e5d7d59f6efee1409d85cd3b1a976d0d83a308be0' => 'TextUI/TestSuiteFilterProcessor.php',
'a01a02eadd18146f12731c7adb8cd56cf76f3f6bda2bae06ff4fd6573789b0f4' => 'Event/Value/ThrowableBuilder.php',
'c78f96e34b98ed01dd8106539d59b8aa8d67f733274118b827c01c5c4111c033' => 'Logging/JUnit/JunitXmlLogger.php',
];

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Pest\Concerns;
use Closure;
use Pest\Exceptions\DatasetArgsCountMismatch;
use Pest\Exceptions\DatasetArgumentsMismatch;
use Pest\Support\ChainableClosure;
use Pest\Support\ExceptionTrace;
use Pest\Support\Reflection;
@ -14,6 +14,7 @@ use PHPUnit\Framework\Attributes\PostCondition;
use PHPUnit\Framework\TestCase;
use ReflectionException;
use ReflectionFunction;
use ReflectionParameter;
use Throwable;
/**
@ -187,7 +188,7 @@ trait Testable
/**
* Gets executed before the Test Case.
*/
protected function setUp(): void
protected function setUp(...$arguments): void
{
TestSuite::getInstance()->test = $this;
@ -221,13 +222,13 @@ trait Testable
$beforeEach = ChainableClosure::bound($this->__beforeEach, $beforeEach);
}
$this->__callClosure($beforeEach, func_get_args());
$this->__callClosure($beforeEach, $arguments);
}
/**
* Gets executed after the Test Case.
*/
protected function tearDown(): void
protected function tearDown(...$arguments): void
{
$afterEach = TestSuite::getInstance()->afterEach->get(self::$__filename);
@ -235,7 +236,7 @@ trait Testable
$afterEach = ChainableClosure::bound($this->__afterEach, $afterEach);
}
$this->__callClosure($afterEach, func_get_args());
$this->__callClosure($afterEach, $arguments);
parent::tearDown();
@ -250,7 +251,7 @@ trait Testable
private function __runTest(Closure $closure, ...$args): mixed
{
$arguments = $this->__resolveTestArguments($args);
$this->__ensureDatasetArgumentNumberMatches($arguments);
$this->__ensureDatasetArgumentNameAndNumberMatches($arguments);
return $this->__callClosure($closure, $arguments);
}
@ -315,9 +316,9 @@ trait Testable
* Ensures dataset items count matches underlying test case required parameters
*
* @throws ReflectionException
* @throws DatasetArgsCountMismatch
* @throws DatasetArgumentsMismatch
*/
private function __ensureDatasetArgumentNumberMatches(array $arguments): void
private function __ensureDatasetArgumentNameAndNumberMatches(array $arguments): void
{
if ($arguments === []) {
return;
@ -328,11 +329,19 @@ trait Testable
$requiredParametersCount = $testReflection->getNumberOfRequiredParameters();
$suppliedParametersCount = count($arguments);
if ($suppliedParametersCount >= $requiredParametersCount) {
$datasetParameterNames = array_keys($arguments);
$testParameterNames = array_map(
fn (ReflectionParameter $reflectionParameter): string => $reflectionParameter->getName(),
array_filter($testReflection->getParameters(), fn (ReflectionParameter $reflectionParameter): bool => ! $reflectionParameter->isOptional()),
);
if (
(count(array_diff($testParameterNames, $datasetParameterNames)) === 0) || isset($testParameterNames[0])
&& $suppliedParametersCount >= $requiredParametersCount) {
return;
}
throw new DatasetArgsCountMismatch($requiredParametersCount, $suppliedParametersCount);
throw new DatasetArgumentsMismatch($requiredParametersCount, $suppliedParametersCount);
}
/**

View File

@ -1,15 +0,0 @@
<?php
declare(strict_types=1);
namespace Pest\Exceptions;
use Exception;
final class DatasetArgsCountMismatch extends Exception
{
public function __construct(int $requiredCount, int $suppliedCount)
{
parent::__construct(sprintf('Test expects %d arguments but dataset only provides %d', $requiredCount, $suppliedCount));
}
}

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Pest\Exceptions;
use Exception;
final class DatasetArgumentsMismatch extends Exception
{
public function __construct(int $requiredCount, int $suppliedCount)
{
if ($requiredCount <= $suppliedCount) {
parent::__construct(sprintf('Test argument names and dataset keys do not match'));
} else {
parent::__construct(sprintf('Test expects %d arguments but dataset only provides %d', $requiredCount, $suppliedCount));
}
}
//
}

View File

@ -100,7 +100,7 @@ final class TestCaseMethodFactory
$method = $this;
return function () use ($testCase, $method, $closure): mixed { // @phpstan-ignore-line
return function (...$arguments) use ($testCase, $method, $closure): mixed { // @phpstan-ignore-line
/* @var TestCase $this */
$testCase->proxies->proxy($this);
$method->proxies->proxy($this);
@ -108,7 +108,7 @@ final class TestCaseMethodFactory
$testCase->chains->chain($this);
$method->chains->chain($this);
return \Pest\Support\Closure::bind($closure, $this, self::class)(...func_get_args());
return \Pest\Support\Closure::bind($closure, $this, self::class)(...$arguments);
};
}
@ -168,13 +168,13 @@ final class TestCaseMethodFactory
return <<<PHP
$attributesCode
public function $methodName()
public function $methodName(...\$arguments)
{
\$test = \Pest\TestSuite::getInstance()->tests->get(self::\$__filename)->getMethod(\$this->name())->getClosure(\$this);
return \$this->__runTest(
\$test,
...func_get_args(),
...\$arguments,
);
}
$datasetsCode

View File

@ -17,13 +17,13 @@ final class ChainableClosure
*/
public static function boundWhen(Closure $condition, Closure $next): Closure
{
return function () use ($condition, $next): void {
return function (...$arguments) use ($condition, $next): void {
if (! is_object($this)) { // @phpstan-ignore-line
throw ShouldNotHappen::fromMessage('$this not bound to chainable closure.');
}
if (\Pest\Support\Closure::bind($condition, $this, self::class)(...func_get_args())) {
\Pest\Support\Closure::bind($next, $this, self::class)(...func_get_args());
if (\Pest\Support\Closure::bind($condition, $this, self::class)(...$arguments)) {
\Pest\Support\Closure::bind($next, $this, self::class)(...$arguments);
}
};
}
@ -33,13 +33,13 @@ final class ChainableClosure
*/
public static function bound(Closure $closure, Closure $next): Closure
{
return function () use ($closure, $next): void {
return function (...$arguments) use ($closure, $next): void {
if (! is_object($this)) { // @phpstan-ignore-line
throw ShouldNotHappen::fromMessage('$this not bound to chainable closure.');
}
\Pest\Support\Closure::bind($closure, $this, self::class)(...func_get_args());
\Pest\Support\Closure::bind($next, $this, self::class)(...func_get_args());
\Pest\Support\Closure::bind($closure, $this, self::class)(...$arguments);
\Pest\Support\Closure::bind($next, $this, self::class)(...$arguments);
};
}
@ -48,9 +48,9 @@ final class ChainableClosure
*/
public static function unbound(Closure $closure, Closure $next): Closure
{
return function () use ($closure, $next): void {
$closure(...func_get_args());
$next(...func_get_args());
return function (...$arguments) use ($closure, $next): void {
$closure(...$arguments);
$next(...$arguments);
};
}
@ -59,9 +59,9 @@ final class ChainableClosure
*/
public static function boundStatically(Closure $closure, Closure $next): Closure
{
return static function () use ($closure, $next): void {
\Pest\Support\Closure::bind($closure, null, self::class)(...func_get_args());
\Pest\Support\Closure::bind($next, null, self::class)(...func_get_args());
return static function (...$arguments) use ($closure, $next): void {
\Pest\Support\Closure::bind($closure, null, self::class)(...$arguments);
\Pest\Support\Closure::bind($next, null, self::class)(...$arguments);
};
}
}

View File

@ -1,5 +1,5 @@
Pest Testing Framework 3.0.0-dev-0005.
Pest Testing Framework 3.0.0-dev-0005.
USAGE: pest <file> [options]
@ -30,11 +30,11 @@
--exclude-group [name] ........... Exclude tests from the specified group(s)
--covers [name] ................. Only run tests that intend to cover [name]
--uses [name] ..................... Only run tests that intend to use [name]
--list-test-files ................................ List available test files
--list-test-files ................................ List available test files
--list-tests .......................................... List available tests
--list-tests-xml [file] ................. List available tests in XML format
--filter [pattern] ............................... Filter which tests to run
--exclude-filter [pattern] .. Exclude tests for the specified filter pattern
--exclude-filter [pattern] .. Exclude tests for the specified filter pattern
--test-suffix [suffixes] Only search for test in files with specified suffix(es). Default: Test.php,.phpt
EXECUTION OPTIONS:

View File

@ -1,3 +1,3 @@
Pest Testing Framework 3.0.0-dev-0005.
Pest Testing Framework 3.0.0-dev-0005.

View File

@ -1060,6 +1060,32 @@
✓ multiple times with multiple dataset dataset "c" / (4) @ repetition 7 of 7
✓ multiple times with multiple dataset dataset "c" / (5) @ repetition 7 of 7
✓ multiple times with multiple dataset dataset "c" / (6) @ repetition 7 of 7
✓ multiple times with iterator @ repetition 1 of 2
✓ multiple times with iterator @ repetition 2 of 2
✓ multiple times with repeat iterator with single dataset ('a') @ repetition 1 of 2
✓ multiple times with repeat iterator with single dataset ('b') @ repetition 1 of 2
✓ multiple times with repeat iterator with single dataset ('c') @ repetition 1 of 2
✓ multiple times with repeat iterator with single dataset ('a') @ repetition 2 of 2
✓ multiple times with repeat iterator with single dataset ('b') @ repetition 2 of 2
✓ multiple times with repeat iterator with single dataset ('c') @ repetition 2 of 2
✓ multiple times with repeat iterator with multiple dataset ('a') / ('d') @ repetition 1 of 2
✓ multiple times with repeat iterator with multiple dataset ('a') / ('e') @ repetition 1 of 2
✓ multiple times with repeat iterator with multiple dataset ('a') / ('f') @ repetition 1 of 2
✓ multiple times with repeat iterator with multiple dataset ('b') / ('d') @ repetition 1 of 2
✓ multiple times with repeat iterator with multiple dataset ('b') / ('e') @ repetition 1 of 2
✓ multiple times with repeat iterator with multiple dataset ('b') / ('f') @ repetition 1 of 2
✓ multiple times with repeat iterator with multiple dataset ('c') / ('d') @ repetition 1 of 2
✓ multiple times with repeat iterator with multiple dataset ('c') / ('e') @ repetition 1 of 2
✓ multiple times with repeat iterator with multiple dataset ('c') / ('f') @ repetition 1 of 2
✓ multiple times with repeat iterator with multiple dataset ('a') / ('d') @ repetition 2 of 2
✓ multiple times with repeat iterator with multiple dataset ('a') / ('e') @ repetition 2 of 2
✓ multiple times with repeat iterator with multiple dataset ('a') / ('f') @ repetition 2 of 2
✓ multiple times with repeat iterator with multiple dataset ('b') / ('d') @ repetition 2 of 2
✓ multiple times with repeat iterator with multiple dataset ('b') / ('e') @ repetition 2 of 2
✓ multiple times with repeat iterator with multiple dataset ('b') / ('f') @ repetition 2 of 2
✓ multiple times with repeat iterator with multiple dataset ('c') / ('d') @ repetition 2 of 2
✓ multiple times with repeat iterator with multiple dataset ('c') / ('e') @ repetition 2 of 2
✓ multiple times with repeat iterator with multiple dataset ('c') / ('f') @ repetition 2 of 2
PASS Tests\Features\ScopedDatasets\Directory\NestedDirectory1\TestFileInNestedDirectoryWithDatasetsFile
✓ uses dataset with (1)
@ -1164,14 +1190,14 @@
PASS Tests\Hooks\BeforeEachTest
✓ global beforeEach execution order
PASS Tests\Overrides\VersionsTest
versions with dataset "Runner/Filter/NameFilterIterator.php"
FAIL Tests\Overrides\VersionsTest
versions with dataset "Runner/Filter/NameFilterIterator.php"
✓ versions with dataset "Runner/ResultCache/DefaultResultCache.php"
✓ versions with dataset "Runner/TestSuiteLoader.php"
versions with dataset "TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php"
versions with dataset "TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php"
versions with dataset "TextUI/TestSuiteFilterProcessor.php"
versions with dataset "Event/Value/ThrowableBuilder.php"
versions with dataset "TextUI/Command/Commands/WarmCodeCoverageCacheCommand.php"
versions with dataset "TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php"
versions with dataset "TextUI/TestSuiteFilterProcessor.php"
versions with dataset "Event/Value/ThrowableBuilder.php"
✓ versions with dataset "Logging/JUnit/JunitXmlLogger.php"
PASS Tests\PHPUnit\CustomAffixes\InvalidTestName
@ -1278,17 +1304,17 @@
✓ it can resolve builtin value types
✓ it cannot resolve a parameter without type
PASS Tests\Unit\Support\DatasetInfo
FAIL Tests\Unit\Support\DatasetInfo
✓ it can check if dataset is defined inside a Datasets directory with ('/var/www/project/tests/Datase…rs.php', true)
✓ it can check if dataset is defined inside a Datasets directory with ('/var/www/project/tests/Datasets.php', false)
✓ it can check if dataset is defined inside a Datasets directory with ('/var/www/project/tests/Featur…rs.php', true)
✓ it can check if dataset is defined inside a Datasets directory with ('/var/www/project/tests/Featur…rs.php', false)
✓ it can check if dataset is defined inside a Datasets directory with ('/var/www/project/tests/Featur…ts.php', false)
it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Datase…rs.php', false)
it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Datasets.php', true)
it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Featur…rs.php', false) #1
it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Featur…rs.php', false) #2
it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Featur…ts.php', true)
it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Datase…rs.php', false)
it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Datasets.php', true)
it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Featur…rs.php', false) #1
it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Featur…rs.php', false) #2
it can check if dataset is defined inside a Datasets.php file with ('/var/www/project/tests/Featur…ts.php', true)
✓ it computes the dataset scope with ('/var/www/project/tests/Datase…rs.php', '/var/www/project/tests')
✓ it computes the dataset scope with ('/var/www/project/tests/Datasets.php', '/var/www/project/tests')
✓ it computes the dataset scope with ('/var/www/project/tests/Featur…rs.php', '/var/www/project/tests/Features')
@ -1385,13 +1411,13 @@
PASS Tests\Visual\Help
✓ visual snapshot of help command output
PASS Tests\Visual\JUnit
FAIL Tests\Visual\JUnit
✓ junit output
junit with parallel
junit with parallel
PASS Tests\Visual\Parallel
parallel
a parallel test can extend another test with same name
WARN Tests\Visual\Parallel
- parallel → Waiting for Parallel to be stable
- a parallel test can extend another test with same name → Waiting for Parallel to be stable
PASS Tests\Visual\SingleTestOrDirectory
✓ allows to run a single test
@ -1413,5 +1439,107 @@
WARN Tests\Visual\Version
- visual snapshot of help command output
────────────────────────────────────────────────────────────────────────────
FAILED Tests\Overrides\VersionsTest > versions with dataset "Runner/Filt…
Failed asserting that two strings are identical.
-'c7b9c8a96006dea314204a8f09a8764e51ce0b9b79aadd58da52e8c328db4870'
+'ec723a9efae521dd6576d2e7d746cc12d3e27f271c49c46420fba8a0e161a61f'
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 13 todos, 24 skipped, 970 passed (2295 assertions)
at tests/Overrides/VersionsTest.php:8
4▕
5▕ use Pest\Bootstrappers\BootOverrides;
6▕
7▕ test('versions', function (string $vendorPath, string $expectedHash) {
➜ 8▕ expect(hash_file('sha256', $vendorPath))->toBe($expectedHash);
9▕ })->with(function () {
10▕ foreach (BootOverrides::FILES as $hash => $file) {
11▕ $path = implode(DIRECTORY_SEPARATOR, [
12▕ dirname(__DIR__, 2),
────────────────────────────────────────────────────────────────────────────
FAILED Tests\Overrides\VersionsTest > versions with dataset "TextUI/Comm…
Failed asserting that two strings are identical.
-'f41e48d6cb546772a7de4f8e66b6b7ce894a5318d063eb52e354d206e96c701c'
+'2ef8e21dbb27cf6597dd9bb0f941c063dcc98b5af2c35d10b1c2d77721582e8f'
at tests/Overrides/VersionsTest.php:8
4▕
5▕ use Pest\Bootstrappers\BootOverrides;
6▕
7▕ test('versions', function (string $vendorPath, string $expectedHash) {
➜ 8▕ expect(hash_file('sha256', $vendorPath))->toBe($expectedHash);
9▕ })->with(function () {
10▕ foreach (BootOverrides::FILES as $hash => $file) {
11▕ $path = implode(DIRECTORY_SEPARATOR, [
12▕ dirname(__DIR__, 2),
────────────────────────────────────────────────────────────────────────────
FAILED Tests\Overrides\VersionsTest > versions with dataset "TextUI/Outp…
Failed asserting that two strings are identical.
-'cb7519f2d82893640b694492cf7ec9528da80773cc1d259634181b5d393528b5'
+'badc88c79c2a47d768be3925051999b158d08b64e57ccf4ce560f1610cbcc1e8'
at tests/Overrides/VersionsTest.php:8
4▕
5▕ use Pest\Bootstrappers\BootOverrides;
6▕
7▕ test('versions', function (string $vendorPath, string $expectedHash) {
➜ 8▕ expect(hash_file('sha256', $vendorPath))->toBe($expectedHash);
9▕ })->with(function () {
10▕ foreach (BootOverrides::FILES as $hash => $file) {
11▕ $path = implode(DIRECTORY_SEPARATOR, [
12▕ dirname(__DIR__, 2),
────────────────────────────────────────────────────────────────────────────
FAILED Tests\Overrides\VersionsTest > versions with dataset "TextUI/Test…
Failed asserting that two strings are identical.
-'6db25ee539e9b12b1fb4e044a0a93410e015bc983ecdd3909cd394fe44ae8c95'
+'8607a62825a762735721c39e5d7d59f6efee1409d85cd3b1a976d0d83a308be0'
at tests/Overrides/VersionsTest.php:8
4▕
5▕ use Pest\Bootstrappers\BootOverrides;
6▕
7▕ test('versions', function (string $vendorPath, string $expectedHash) {
➜ 8▕ expect(hash_file('sha256', $vendorPath))->toBe($expectedHash);
9▕ })->with(function () {
10▕ foreach (BootOverrides::FILES as $hash => $file) {
11▕ $path = implode(DIRECTORY_SEPARATOR, [
12▕ dirname(__DIR__, 2),
────────────────────────────────────────────────────────────────────────────
FAILED Tests\Overrides\VersionsTest > versions with dataset "Event/Value…
Failed asserting that two strings are identical.
-'ef64a657ed9c0067791483784944107827bf227c7e3200f212b6751876b99e25'
+'a01a02eadd18146f12731c7adb8cd56cf76f3f6bda2bae06ff4fd6573789b0f4'
at tests/Overrides/VersionsTest.php:8
4▕
5▕ use Pest\Bootstrappers\BootOverrides;
6▕
7▕ test('versions', function (string $vendorPath, string $expectedHash) {
➜ 8▕ expect(hash_file('sha256', $vendorPath))->toBe($expectedHash);
9▕ })->with(function () {
10▕ foreach (BootOverrides::FILES as $hash => $file) {
11▕ $path = implode(DIRECTORY_SEPARATOR, [
12▕ dirname(__DIR__, 2),
────────────────────────────────────────────────────────────────────────────
FAILED Tests\Unit\Support\DatasetInfo > it can check if dataset i… Error
Unknown named parameter $file
str_replace(): Argument #3 ($subject) must be of type array|string, null given
at vendor/filp/whoops/src/Whoops/Exception/FrameCollection.php:43
39▕ * @return FrameCollection
40▕ */
41▕ public function filter($callable)
42▕ {
➜ 43▕ $this->frames = array_values(array_filter($this->frames, $callable));
44▕ return $this;
45▕ }
46▕

View File

@ -12,8 +12,8 @@ it('can check if dataset is defined inside a Datasets directory', function (stri
['file' => '/var/www/project/tests/Features/Datasets.php', 'inside' => false],
]);
it('can check if dataset is defined inside a Datasets.php file', function (string $path, bool $inside) {
expect(DatasetInfo::isADatasetsFile($path))->toBe($inside);
it('can check if dataset is defined inside a Datasets.php file', function (string $file, bool $inside) {
expect(DatasetInfo::isADatasetsFile($file))->toBe($inside);
})->with([
['file' => '/var/www/project/tests/Datasets/Numbers.php', 'inside' => false],
['file' => '/var/www/project/tests/Datasets.php', 'inside' => true],