mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 15:57:21 +01:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fe79d9c18 | |||
| 2744da4292 | |||
| 87f4e5e7b3 | |||
| bb3decf3cc | |||
| 4e2987d438 | |||
| a25158bce8 | |||
| 49e77b1d4c | |||
| 989e43d1a0 | |||
| 7cd42aafd8 | |||
| 48a1de273f | |||
| 970e16e949 | |||
| 432ff221c6 | |||
| a55da85dd2 | |||
| f291cd1603 | |||
| 5de0c2254a | |||
| b98ce0ced3 | |||
| 28772c2609 | |||
| 452ffaf8df | |||
| e8338405b5 | |||
| 1b014e4b18 | |||
| 034715e8b1 | |||
| 09eff785c4 | |||
| 22cc7805d7 | |||
| 669dc0da71 | |||
| 689da4ed4e | |||
| 2f15861b0d | |||
| 0d50d35b5e | |||
| ce61ced8e1 | |||
| 7227d24611 | |||
| 45f16484d5 | |||
| b16e8650da | |||
| c2f30e0148 | |||
| 47ce45de56 | |||
| 32881774d2 |
13
.github/workflows/tests.yml
vendored
13
.github/workflows/tests.yml
vendored
@ -13,8 +13,8 @@ jobs:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
symfony: ['7.0']
|
||||
php: ['8.2', '8.3']
|
||||
symfony: ['7.1']
|
||||
php: ['8.2', '8.3', '8.4']
|
||||
dependency_version: [prefer-lowest, prefer-stable]
|
||||
|
||||
name: PHP ${{ matrix.php }} - Symfony ^${{ matrix.symfony }} - ${{ matrix.os }} - ${{ matrix.dependency_version }}
|
||||
@ -36,7 +36,13 @@ jobs:
|
||||
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
|
||||
- name: Install PHP dependencies
|
||||
run: composer update --${{ matrix.dependency_version }} --no-interaction --no-progress --ansi --with="symfony/console:~${{ matrix.symfony }}"
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${{ matrix.php }}" == "8.4" ]]; then
|
||||
composer update --${{ matrix.dependency_version }} --no-interaction --no-progress --ansi --with="symfony/console:^${{ matrix.symfony }}" --ignore-platform-req=php
|
||||
else
|
||||
composer update --${{ matrix.dependency_version }} --no-interaction --no-progress --ansi --with="symfony/console:^${{ matrix.symfony }}"
|
||||
fi
|
||||
|
||||
- name: Unit Tests
|
||||
run: composer test:unit
|
||||
@ -45,4 +51,5 @@ jobs:
|
||||
run: composer test:parallel
|
||||
|
||||
- name: Integration Tests
|
||||
if: ${{ matrix.php != '8.4' }}
|
||||
run: composer test:integration
|
||||
|
||||
@ -18,16 +18,16 @@
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.2.0",
|
||||
"brianium/paratest": "^7.5.4",
|
||||
"brianium/paratest": "^7.5.5",
|
||||
"nunomaduro/collision": "^8.4.0",
|
||||
"nunomaduro/termwind": "^2.1.0",
|
||||
"pestphp/pest-plugin": "^3.0.0",
|
||||
"pestphp/pest-plugin-arch": "^3.0.0",
|
||||
"pestphp/pest-plugin-mutate": "^3.0.3",
|
||||
"phpunit/phpunit": "^11.3.4"
|
||||
"pestphp/pest-plugin-mutate": "^3.0.5",
|
||||
"phpunit/phpunit": "^11.3.6"
|
||||
},
|
||||
"conflict": {
|
||||
"phpunit/phpunit": ">11.3.4",
|
||||
"phpunit/phpunit": ">11.3.6",
|
||||
"sebastian/exporter": "<6.0.0",
|
||||
"webmozart/assert": "<1.11.0"
|
||||
},
|
||||
@ -54,7 +54,7 @@
|
||||
"require-dev": {
|
||||
"pestphp/pest-dev-tools": "^3.0.0",
|
||||
"pestphp/pest-plugin-type-coverage": "^3.0.0",
|
||||
"symfony/process": "^7.1.3"
|
||||
"symfony/process": "^7.1.5"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true,
|
||||
|
||||
@ -27,17 +27,20 @@ final class Laravel extends AbstractPreset
|
||||
->ignoring('App\Enums');
|
||||
|
||||
$this->expectations[] = expect('App\Enums')
|
||||
->toBeEnums();
|
||||
->toBeEnums()
|
||||
->ignoring('App\Enums\Concerns');
|
||||
|
||||
$this->expectations[] = expect('App\Features')
|
||||
->toBeClasses();
|
||||
->toBeClasses()
|
||||
->ignoring('App\Features\Concerns');
|
||||
|
||||
$this->expectations[] = expect('App\Features')
|
||||
->toHaveMethod('resolve');
|
||||
|
||||
$this->expectations[] = expect('App\Exceptions')
|
||||
->classes()
|
||||
->toImplement('Throwable');
|
||||
->toImplement('Throwable')
|
||||
->ignoring('App\Exceptions\Handler');
|
||||
|
||||
$this->expectations[] = expect('App')
|
||||
->not->toImplement(Throwable::class)
|
||||
|
||||
@ -21,6 +21,7 @@ final class Strict extends AbstractPreset
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->classes()->not->toHaveProtectedMethods(),
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->classes()->not->toBeAbstract(),
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->toUseStrictTypes(),
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->toUseStrictEquality(),
|
||||
fn (Expectation $namespace): ArchExpectation => $namespace->classes()->toBeFinal(),
|
||||
);
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ final class BootOverrides implements Bootstrapper
|
||||
'8abdad6413329c6fe0d7d44a8b9926e390af32c0b3123f3720bb9c5bbc6fbb7e' => 'TextUI/Output/Default/ProgressPrinter/Subscriber/TestSkippedSubscriber.php',
|
||||
'43883b7e5811886cf3731c8ed6304d5a77078d9731e1e505abc2da36bde19f3e' => 'TextUI/TestSuiteFilterProcessor.php',
|
||||
'357d5cd7007f8559b26e1b8cdf43bb6fb15b51b79db981779da6f31b7ec39dad' => 'Event/Value/ThrowableBuilder.php',
|
||||
'01974a686eba69b5fbb87a904d936eae2176e39567616898c5b758db71d87a22' => 'Logging/JUnit/JunitXmlLogger.php',
|
||||
'676273f1fe483877cf2d95c5aedbf9ae5d6a8e2f4c12d6ce716df6591e6db023' => 'Logging/JUnit/JunitXmlLogger.php',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@ -116,7 +116,7 @@ trait Testable
|
||||
self::$__latestIssues = $method->issues;
|
||||
self::$__latestPrs = $method->prs;
|
||||
$this->__describing = $method->describing;
|
||||
$this->__test = $method->getClosure($this);
|
||||
$this->__test = $method->getClosure();
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,6 +240,8 @@ trait Testable
|
||||
|
||||
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
|
||||
|
||||
$method->setUp($this);
|
||||
|
||||
$description = $method->description;
|
||||
if ($this->dataName()) {
|
||||
$description = str_contains((string) $description, ':dataset')
|
||||
@ -298,6 +300,9 @@ trait Testable
|
||||
parent::tearDown();
|
||||
|
||||
TestSuite::getInstance()->test = null;
|
||||
|
||||
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
|
||||
$method->tearDown($this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,11 +397,12 @@ trait Testable
|
||||
fn (ReflectionParameter $reflectionParameter): string => $reflectionParameter->getName(),
|
||||
array_filter($testReflection->getParameters(), fn (ReflectionParameter $reflectionParameter): bool => ! $reflectionParameter->isOptional()),
|
||||
);
|
||||
|
||||
if (array_diff($testParameterNames, $datasetParameterNames) === []) {
|
||||
return;
|
||||
}
|
||||
if (isset($testParameterNames[0])
|
||||
&& $suppliedParametersCount >= $requiredParametersCount) {
|
||||
|
||||
if (isset($testParameterNames[0]) && $suppliedParametersCount >= $requiredParametersCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -89,7 +89,7 @@ final class Project
|
||||
{
|
||||
$this->issues = "https://{$namespace}.atlassian.net/browse/{$project}-%s";
|
||||
|
||||
$this->assignees = "https://{$namespace}.atlassian.net/secure/ViewProfile?name=%s";
|
||||
$this->assignees = "https://{$namespace}.atlassian.net/secure/ViewProfile.jspa?name=%s";
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -223,7 +223,7 @@ final class Expectation
|
||||
throw new BadMethodCallException('Expectation value is not iterable.');
|
||||
}
|
||||
|
||||
if (count($callbacks) == 0) {
|
||||
if ($callbacks === []) {
|
||||
throw new InvalidArgumentException('No sequence expectations defined.');
|
||||
}
|
||||
|
||||
@ -264,7 +264,7 @@ final class Expectation
|
||||
$matched = false;
|
||||
|
||||
foreach ($expressions as $key => $callback) {
|
||||
if ($subject != $key) {
|
||||
if ($subject != $key) { // @pest-arch-ignore-line
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -380,7 +380,7 @@ final class Expectation
|
||||
if (self::hasExtend($name)) {
|
||||
$extend = self::$extends[$name]->bindTo($this, Expectation::class);
|
||||
|
||||
if ($extend != false) {
|
||||
if ($extend != false) { // @pest-arch-ignore-line
|
||||
return $extend;
|
||||
}
|
||||
}
|
||||
@ -515,6 +515,19 @@ final class Expectation
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target uses strict equality.
|
||||
*/
|
||||
public function toUseStrictEquality(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this,
|
||||
fn (ObjectDescription $object): bool => ! str_contains((string) file_get_contents($object->path), ' == ') && ! str_contains((string) file_get_contents($object->path), ' != '),
|
||||
'to use strict equality',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, ' == ') || str_contains($line, ' != ')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target is final.
|
||||
*/
|
||||
|
||||
@ -152,6 +152,19 @@ final readonly class OppositeExpectation
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target does not use the strict equality operator.
|
||||
*/
|
||||
public function toUseStrictEquality(): ArchExpectation
|
||||
{
|
||||
return Targeted::make(
|
||||
$this->original,
|
||||
fn (ObjectDescription $object): bool => ! str_contains((string) file_get_contents($object->path), ' === ') && ! str_contains((string) file_get_contents($object->path), ' !== '),
|
||||
'to use strict equality',
|
||||
FileLineFinder::where(fn (string $line): bool => str_contains($line, ' === ') || str_contains($line, ' !== ')),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given expectation target is not final.
|
||||
*/
|
||||
|
||||
@ -118,9 +118,9 @@ final class TestCaseMethodFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the test's closure.
|
||||
* Sets the test's hooks, and runs any proxy to the test case.
|
||||
*/
|
||||
public function getClosure(TestCase $concrete): Closure
|
||||
public function setUp(TestCase $concrete): void
|
||||
{
|
||||
$concrete::flush(); // @phpstan-ignore-line
|
||||
|
||||
@ -128,14 +128,29 @@ final class TestCaseMethodFactory
|
||||
throw ShouldNotHappen::fromMessage('Description can not be empty.');
|
||||
}
|
||||
|
||||
$closure = $this->closure;
|
||||
|
||||
$testCase = TestSuite::getInstance()->tests->get($this->filename);
|
||||
|
||||
assert($testCase instanceof TestCaseFactory);
|
||||
$testCase->factoryProxies->proxy($concrete);
|
||||
$this->factoryProxies->proxy($concrete);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the test case.
|
||||
*/
|
||||
public function tearDown(TestCase $concrete): void
|
||||
{
|
||||
$concrete::flush(); // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the test's closure.
|
||||
*/
|
||||
public function getClosure(): Closure
|
||||
{
|
||||
$closure = $this->closure;
|
||||
$testCase = TestSuite::getInstance()->tests->get($this->filename);
|
||||
assert($testCase instanceof TestCaseFactory);
|
||||
$method = $this;
|
||||
|
||||
return function (...$arguments) use ($testCase, $method, $closure): mixed { // @phpstan-ignore-line
|
||||
@ -209,10 +224,8 @@ final class TestCaseMethodFactory
|
||||
$attributesCode
|
||||
public function $methodName(...\$arguments)
|
||||
{
|
||||
\$test = \Pest\TestSuite::getInstance()->tests->get(self::\$__filename)->getMethod(\$this->name())->getClosure(\$this);
|
||||
|
||||
return \$this->__runTest(
|
||||
\$test,
|
||||
\$this->__test,
|
||||
...\$arguments,
|
||||
);
|
||||
}
|
||||
|
||||
@ -217,7 +217,7 @@ if (! function_exists('afterAll')) {
|
||||
|
||||
if (! function_exists('covers')) {
|
||||
/**
|
||||
* Specifies which classes, or functions, a test method covers.
|
||||
* Specifies which classes, or functions, a test case covers.
|
||||
*
|
||||
* @param array<int, string>|string $classesOrFunctions
|
||||
*/
|
||||
@ -243,3 +243,38 @@ if (! function_exists('covers')) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('mutates')) {
|
||||
/**
|
||||
* Specifies which classes, enums, or traits a test case mutates.
|
||||
*
|
||||
* @param array<int, string>|string $targets
|
||||
*/
|
||||
function mutates(array|string ...$targets): void
|
||||
{
|
||||
$filename = Backtrace::file();
|
||||
|
||||
$beforeEachCall = (new BeforeEachCall(TestSuite::getInstance(), $filename));
|
||||
$beforeEachCall->group('__pest_mutate_only');
|
||||
|
||||
/** @var MutationTestRunner $runner */
|
||||
$runner = Container::getInstance()->get(MutationTestRunner::class);
|
||||
/** @var \Pest\Mutate\Repositories\ConfigurationRepository $configurationRepository */
|
||||
$configurationRepository = Container::getInstance()->get(ConfigurationRepository::class);
|
||||
$everything = $configurationRepository->cliConfiguration->toArray()['everything'] ?? false;
|
||||
$classes = $configurationRepository->cliConfiguration->toArray()['classes'] ?? false;
|
||||
$paths = $configurationRepository->cliConfiguration->toArray()['paths'] ?? false;
|
||||
|
||||
if ($runner->isEnabled() && ! $everything && ! is_array($classes) && ! is_array($paths)) {
|
||||
$beforeEachCall->only('__pest_mutate_only');
|
||||
}
|
||||
|
||||
/** @var ConfigurationRepository $configurationRepository */
|
||||
$configurationRepository = Container::getInstance()->get(ConfigurationRepository::class);
|
||||
$paths = $configurationRepository->cliConfiguration->toArray()['paths'] ?? false;
|
||||
|
||||
if (! is_array($paths)) {
|
||||
$configurationRepository->globalConfiguration('default')->class(...$targets); // @phpstan-ignore-line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ use PHPUnit\Event\Test\Failed;
|
||||
use PHPUnit\Event\Test\MarkedIncomplete;
|
||||
use PHPUnit\Event\Test\Skipped;
|
||||
use PHPUnit\Event\TestSuite\TestSuite;
|
||||
use PHPUnit\Event\TestSuite\TestSuiteForTestMethodWithDataProvider;
|
||||
use PHPUnit\Framework\Exception as FrameworkException;
|
||||
use PHPUnit\TestRunner\TestResult\TestResult as PhpUnitTestResult;
|
||||
|
||||
@ -147,6 +148,13 @@ final readonly class Converter
|
||||
*/
|
||||
public function getTestSuiteName(TestSuite $testSuite): string
|
||||
{
|
||||
if ($testSuite instanceof TestSuiteForTestMethodWithDataProvider) {
|
||||
$firstTest = $this->getFirstTest($testSuite);
|
||||
if ($firstTest instanceof \PHPUnit\Event\Code\TestMethod) {
|
||||
return $this->getTestMethodNameWithoutDatasetSuffix($firstTest);
|
||||
}
|
||||
}
|
||||
|
||||
$name = $testSuite->name();
|
||||
|
||||
if (! str_starts_with($name, self::PREFIX)) {
|
||||
@ -168,6 +176,35 @@ final readonly class Converter
|
||||
* Gets the test suite location.
|
||||
*/
|
||||
public function getTestSuiteLocation(TestSuite $testSuite): ?string
|
||||
{
|
||||
$firstTest = $this->getFirstTest($testSuite);
|
||||
if (! $firstTest instanceof \PHPUnit\Event\Code\TestMethod) {
|
||||
return null;
|
||||
}
|
||||
$path = $firstTest->testDox()->prettifiedClassName();
|
||||
$classRelativePath = $this->toRelativePath($path);
|
||||
|
||||
if ($testSuite instanceof TestSuiteForTestMethodWithDataProvider) {
|
||||
$methodName = $this->getTestMethodNameWithoutDatasetSuffix($firstTest);
|
||||
|
||||
return "$classRelativePath::$methodName";
|
||||
}
|
||||
|
||||
return $classRelativePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the prettified test method name without dataset-related suffix.
|
||||
*/
|
||||
private function getTestMethodNameWithoutDatasetSuffix(TestMethod $testMethod): string
|
||||
{
|
||||
return Str::beforeLast($testMethod->testDox()->prettifiedMethodName(), ' with data set ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first test from the test suite.
|
||||
*/
|
||||
private function getFirstTest(TestSuite $testSuite): ?TestMethod
|
||||
{
|
||||
$tests = $testSuite->tests()->asArray();
|
||||
|
||||
@ -181,9 +218,7 @@ final readonly class Converter
|
||||
throw ShouldNotHappen::fromMessage('Not an instance of TestMethod');
|
||||
}
|
||||
|
||||
$path = $firstTest->testDox()->prettifiedClassName();
|
||||
|
||||
return $this->toRelativePath($path);
|
||||
return $firstTest;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -38,7 +38,7 @@ final class ServiceMessage
|
||||
{
|
||||
return new self('testSuiteStarted', [
|
||||
'name' => $name,
|
||||
'locationHint' => $location === null ? null : "file://$location",
|
||||
'locationHint' => $location === null ? null : "pest_qn://$location",
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Pest\PendingCalls;
|
||||
|
||||
use Closure;
|
||||
use Pest\Concerns\Testable;
|
||||
use Pest\Exceptions\InvalidArgumentException;
|
||||
use Pest\Exceptions\TestDescriptionMissing;
|
||||
use Pest\Factories\Attribute;
|
||||
@ -25,9 +26,9 @@ use PHPUnit\Framework\TestCase;
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @mixin HigherOrderCallables|TestCase
|
||||
* @mixin HigherOrderCallables|TestCase|Testable
|
||||
*/
|
||||
final class TestCall
|
||||
final class TestCall // @phpstan-ignore-line
|
||||
{
|
||||
use Describable;
|
||||
|
||||
@ -358,8 +359,8 @@ final class TestCall
|
||||
public function todo(// @phpstan-ignore-line
|
||||
array|string|null $note = null,
|
||||
array|string|null $assignee = null,
|
||||
array|string|null $issue = null,
|
||||
array|string|null $pr = null,
|
||||
array|string|int|null $issue = null,
|
||||
array|string|int|null $pr = null,
|
||||
): self {
|
||||
$this->skip('__TODO__');
|
||||
|
||||
@ -390,8 +391,8 @@ final class TestCall
|
||||
public function wip(// @phpstan-ignore-line
|
||||
array|string|null $note = null,
|
||||
array|string|null $assignee = null,
|
||||
array|string|null $issue = null,
|
||||
array|string|null $pr = null,
|
||||
array|string|int|null $issue = null,
|
||||
array|string|int|null $pr = null,
|
||||
): self {
|
||||
if ($issue !== null) {
|
||||
$this->issue($issue);
|
||||
@ -418,8 +419,8 @@ final class TestCall
|
||||
public function done(// @phpstan-ignore-line
|
||||
array|string|null $note = null,
|
||||
array|string|null $assignee = null,
|
||||
array|string|null $issue = null,
|
||||
array|string|null $pr = null,
|
||||
array|string|int|null $issue = null,
|
||||
array|string|int|null $pr = null,
|
||||
): self {
|
||||
if ($issue !== null) {
|
||||
$this->issue($issue);
|
||||
|
||||
@ -6,7 +6,7 @@ namespace Pest;
|
||||
|
||||
function version(): string
|
||||
{
|
||||
return '3.0.3';
|
||||
return '3.2.4';
|
||||
}
|
||||
|
||||
function testDirectory(string $file = ''): string
|
||||
|
||||
@ -122,6 +122,9 @@ final class WrapperRunner implements RunnerInterface
|
||||
$parameters = array_merge($parameters, $options->passthruPhp);
|
||||
}
|
||||
|
||||
/** @var array<int, non-empty-string> $parameters */
|
||||
$parameters = $this->handleLaravelHerd($parameters);
|
||||
|
||||
$parameters[] = $wrapper;
|
||||
|
||||
$this->parameters = $parameters;
|
||||
@ -153,6 +156,21 @@ final class WrapperRunner implements RunnerInterface
|
||||
return $this->complete($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Laravel Herd's debug and coverage modes.
|
||||
*
|
||||
* @param array<string> $parameters
|
||||
* @return array<string>
|
||||
*/
|
||||
private function handleLaravelHerd(array $parameters): array
|
||||
{
|
||||
if (isset($_ENV['HERD_DEBUG_INI'])) {
|
||||
return array_merge($parameters, ['-c', $_ENV['HERD_DEBUG_INI']]);
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
private function startWorkers(): void
|
||||
{
|
||||
for ($token = 1; $token <= $this->options->processes; $token++) {
|
||||
|
||||
@ -20,13 +20,13 @@ final class Closure
|
||||
*/
|
||||
public static function bind(?BaseClosure $closure, ?object $newThis, object|string|null $newScope = 'static'): BaseClosure
|
||||
{
|
||||
if ($closure == null) {
|
||||
if (! $closure instanceof \Closure) {
|
||||
throw ShouldNotHappen::fromMessage('Could not bind null closure.');
|
||||
}
|
||||
|
||||
$closure = BaseClosure::bind($closure, $newThis, $newScope);
|
||||
|
||||
if ($closure == false) {
|
||||
if (! $closure instanceof \Closure) {
|
||||
throw ShouldNotHappen::fromMessage('Could not bind closure.');
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
Pest Testing Framework 3.0.3.
|
||||
Pest Testing Framework 3.2.4.
|
||||
|
||||
USAGE: pest <file> [options]
|
||||
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
|
||||
Pest Testing Framework 3.0.3.
|
||||
Pest Testing Framework 3.2.4.
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
##teamcity[testSuiteStarted name='Tests/tests/Failure' locationHint='file://tests/.tests/Failure.php' flowId='1234']
|
||||
##teamcity[testSuiteStarted name='Tests/tests/Failure' locationHint='pest_qn://tests/.tests/Failure.php' flowId='1234']
|
||||
##teamcity[testCount count='8' flowId='1234']
|
||||
##teamcity[testStarted name='it can fail with comparison' locationHint='pest_qn://tests/.tests/Failure.php::it can fail with comparison' flowId='1234']
|
||||
##teamcity[testFailed name='it can fail with comparison' message='Failed asserting that true matches expected false.' details='at tests/.tests/Failure.php:6' type='comparisonFailure' actual='true' expected='false' flowId='1234']
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
##teamcity[testSuiteStarted name='Tests/tests/SuccessOnly' locationHint='file://tests/.tests/SuccessOnly.php' flowId='1234']
|
||||
##teamcity[testCount count='2' flowId='1234']
|
||||
##teamcity[testSuiteStarted name='Tests/tests/SuccessOnly' locationHint='pest_qn://tests/.tests/SuccessOnly.php' flowId='1234']
|
||||
##teamcity[testCount count='3' flowId='1234']
|
||||
##teamcity[testStarted name='it can pass with comparison' locationHint='pest_qn://tests/.tests/SuccessOnly.php::it can pass with comparison' flowId='1234']
|
||||
##teamcity[testFinished name='it can pass with comparison' duration='100000' flowId='1234']
|
||||
##teamcity[testStarted name='can also pass' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can also pass' flowId='1234']
|
||||
##teamcity[testFinished name='can also pass' duration='100000' flowId='1234']
|
||||
##teamcity[testSuiteStarted name='can pass with dataset' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can pass with dataset' flowId='1234']
|
||||
##teamcity[testStarted name='can pass with dataset with data set "(true)"' locationHint='pest_qn://tests/.tests/SuccessOnly.php::can pass with dataset with data set "(true)"' flowId='1234']
|
||||
##teamcity[testFinished name='can pass with dataset with data set "(true)"' duration='100000' flowId='1234']
|
||||
##teamcity[testSuiteFinished name='can pass with dataset' flowId='1234']
|
||||
##teamcity[testSuiteFinished name='Tests/tests/SuccessOnly' flowId='1234']
|
||||
|
||||
[90mTests:[39m [32;1m2 passed[39;22m[90m (2 assertions)[39m
|
||||
[90mTests:[39m [32;1m3 passed[39;22m[90m (3 assertions)[39m
|
||||
[90mDuration:[39m [39m1.00s[39m
|
||||
|
||||
|
||||
@ -968,6 +968,12 @@
|
||||
✓ it can handle a non-defined exception
|
||||
✓ it can handle a class not found Error
|
||||
|
||||
PASS Tests\Features\Expect\toUseStrictEquality
|
||||
✓ missing strict equality
|
||||
✓ has strict equality
|
||||
✓ opposite missing strict equality
|
||||
✓ opposite has strict equality
|
||||
|
||||
PASS Tests\Features\Expect\toUseTrait
|
||||
✓ pass
|
||||
✓ failures
|
||||
@ -1305,6 +1311,7 @@
|
||||
✓ it executes tests in the Helpers directory
|
||||
|
||||
PASS Tests\Hooks\AfterEachTest
|
||||
✓ nested → nested afterEach execution order
|
||||
✓ global afterEach execution order
|
||||
|
||||
PASS Tests\Hooks\BeforeEachTest
|
||||
@ -1573,4 +1580,4 @@
|
||||
WARN Tests\Visual\Version
|
||||
- visual snapshot of help command output
|
||||
|
||||
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 17 todos, 28 skipped, 1088 passed (2615 assertions)
|
||||
Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 17 todos, 28 skipped, 1093 passed (2644 assertions)
|
||||
@ -9,3 +9,7 @@ it('can pass with comparison', function () {
|
||||
test('can also pass', function () {
|
||||
expect("string")->toBeString();
|
||||
});
|
||||
|
||||
test('can pass with dataset', function ($value) {
|
||||
expect($value)->toEqual(true);
|
||||
})->with([true]);
|
||||
|
||||
21
tests/Features/Expect/toUseStrictEquality.php
Normal file
21
tests/Features/Expect/toUseStrictEquality.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
use Pest\Arch\Exceptions\ArchExpectationFailedException;
|
||||
|
||||
test('missing strict equality')
|
||||
->throws(ArchExpectationFailedException::class)
|
||||
->expect('Tests\\Fixtures\\Arch\\ToUseStrictEquality\\NotStrictEquality')
|
||||
->toUseStrictEquality();
|
||||
|
||||
test('has strict equality')
|
||||
->expect('Tests\\Fixtures\\Arch\\ToUseStrictEquality\\StrictEquality')
|
||||
->toUseStrictEquality();
|
||||
|
||||
test('opposite missing strict equality')
|
||||
->throws(ArchExpectationFailedException::class)
|
||||
->expect('Tests\\Fixtures\\Arch\\ToUseStrictEquality\\StrictEquality')
|
||||
->not->toUseStrictEquality();
|
||||
|
||||
test('opposite has strict equality')
|
||||
->expect('Tests\\Fixtures\\Arch\\ToUseStrictEquality\\NotStrictEquality')
|
||||
->not->toUseStrictEquality();
|
||||
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Fixtures\Arch\ToUseStrictEquality;
|
||||
|
||||
class NotStrictEquality
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$a = 1;
|
||||
$b = '1';
|
||||
|
||||
if ($a == $b) {
|
||||
echo 'Equal';
|
||||
}
|
||||
|
||||
if ($a != $b) {
|
||||
echo 'Equal';
|
||||
}
|
||||
}
|
||||
}
|
||||
22
tests/Fixtures/Arch/ToUseStrictEquality/StrictEquality.php
Normal file
22
tests/Fixtures/Arch/ToUseStrictEquality/StrictEquality.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Fixtures\Arch\ToUseStrictEquality;
|
||||
|
||||
class StrictEquality
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$a = 1;
|
||||
$b = '1';
|
||||
|
||||
if ($a === $b) {
|
||||
echo 'Equal';
|
||||
}
|
||||
|
||||
if ($a !== $b) {
|
||||
echo 'Equal';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,23 +1,79 @@
|
||||
<?php
|
||||
|
||||
beforeEach(function () {
|
||||
$this->ith = 0;
|
||||
});
|
||||
|
||||
pest()->afterEach(function () {
|
||||
expect($this)
|
||||
->toHaveProperty('ith')
|
||||
->and($this->ith)
|
||||
->toBe(1);
|
||||
->toBe(3);
|
||||
|
||||
$this->ith = 2;
|
||||
$this->ith++;
|
||||
});
|
||||
|
||||
pest()->afterEach(function () {
|
||||
expect($this)
|
||||
->toHaveProperty('ith')
|
||||
->and($this->ith)
|
||||
->toBe(4);
|
||||
|
||||
$this->ith++;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
expect($this)
|
||||
->toHaveProperty('ith')
|
||||
->and($this->ith)
|
||||
->toBe(2);
|
||||
->toBe(5);
|
||||
|
||||
$this->ith++;
|
||||
});
|
||||
|
||||
describe('nested', function () {
|
||||
afterEach(function () {
|
||||
expect($this)
|
||||
->toHaveProperty('ith')
|
||||
->and($this->ith)
|
||||
->toBe(6);
|
||||
|
||||
$this->ith++;
|
||||
});
|
||||
|
||||
test('nested afterEach execution order', function () {
|
||||
expect($this)
|
||||
->toHaveProperty('ith')
|
||||
->and($this->ith)
|
||||
->toBe(0);
|
||||
|
||||
$this->ith++;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
expect($this)
|
||||
->toHaveProperty('ith')
|
||||
->and($this->ith)
|
||||
->toBe(7);
|
||||
|
||||
$this->ith++;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
expect($this)
|
||||
->toHaveProperty('ith')
|
||||
->and($this->ith)
|
||||
->toBeBetween(6, 8);
|
||||
|
||||
$this->ith++;
|
||||
});
|
||||
|
||||
test('global afterEach execution order', function () {
|
||||
expect($this)
|
||||
->not()
|
||||
->toHaveProperty('ith');
|
||||
->toHaveProperty('ith')
|
||||
->and($this->ith)
|
||||
->toBe(0);
|
||||
|
||||
$this->ith++;
|
||||
});
|
||||
|
||||
@ -32,7 +32,12 @@ pest()
|
||||
$_SERVER['globalHook']->calls->beforeAll++;
|
||||
})
|
||||
->afterEach(function () {
|
||||
$this->ith = 0;
|
||||
if (! isset($this->ith)) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert($this->ith === 1, 'Expected $this->ith to be 1, but got '.$this->ith);
|
||||
$this->ith++;
|
||||
})
|
||||
->afterAll(function () {
|
||||
$_SERVER['globalHook']->afterAll = 0;
|
||||
@ -57,12 +62,12 @@ pest()->in('Hooks')
|
||||
$_SERVER['globalHook']->beforeAll = 1;
|
||||
})
|
||||
->afterEach(function () {
|
||||
expect($this)
|
||||
->toHaveProperty('ith')
|
||||
->and($this->ith)
|
||||
->toBe(0);
|
||||
if (! isset($this->ith)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->ith = 1;
|
||||
assert($this->ith === 2, 'Expected $this->ith to be 1, but got '.$this->ith);
|
||||
$this->ith++;
|
||||
})
|
||||
->afterAll(function () {
|
||||
expect($_SERVER['globalHook'])
|
||||
|
||||
@ -36,8 +36,8 @@ test('junit output', function () use ($normalizedPath, $run) {
|
||||
expect($result['testsuite']['@attributes'])
|
||||
->name->toBe('Tests\tests\SuccessOnly')
|
||||
->file->toBe($normalizedPath('tests/.tests/SuccessOnly.php'))
|
||||
->tests->toBe('2')
|
||||
->assertions->toBe('2')
|
||||
->tests->toBe('3')
|
||||
->assertions->toBe('3')
|
||||
->errors->toBe('0')
|
||||
->failures->toBe('0')
|
||||
->skipped->toBe('0');
|
||||
|
||||
@ -16,7 +16,7 @@ $run = function () {
|
||||
|
||||
test('parallel', function () use ($run) {
|
||||
expect($run('--exclude-group=integration'))
|
||||
->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 17 todos, 19 skipped, 1078 passed (2591 assertions)')
|
||||
->toContain('Tests: 2 deprecated, 4 warnings, 5 incomplete, 2 notices, 17 todos, 19 skipped, 1083 passed (2620 assertions)')
|
||||
->toContain('Parallel: 3 processes');
|
||||
})->skipOnWindows();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user