Uses Collision ^7.0

This commit is contained in:
Nuno Maduro
2022-09-15 01:07:15 +01:00
parent eab944023c
commit 3ff95faaaa
47 changed files with 646 additions and 308 deletions

View File

@ -2,3 +2,6 @@
2. Support for `default` printer.
3. Support for `TeamCity` printer.
4. Support for `JUnit` log.
5. Plugins
6. Parallel
7. Collision's todo...

View File

@ -11,6 +11,9 @@ use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
(static function () {
// Ensures Collision's Printer is registered.
$_SERVER['COLLISION_PRINTER'] = 'DefaultPrinter';
// Used when Pest is required using composer.
$vendorPath = dirname(__DIR__, 4) . '/vendor/autoload.php';

View File

@ -18,7 +18,7 @@
],
"require": {
"php": "^8.1.0",
"nunomaduro/collision": "^6.3",
"nunomaduro/collision": "dev-next",
"pestphp/pest-plugin": "^1.0.0",
"phpunit/phpunit": "10.0.x-dev"
},
@ -86,6 +86,7 @@
"Pest\\Plugins\\Init",
"Pest\\Plugins\\Environment",
"Pest\\Plugins\\Memory",
"Pest\\Plugins\\Printer",
"Pest\\Plugins\\Retry",
"Pest\\Plugins\\Version"
]

View File

@ -44,14 +44,13 @@ use function basename;
use function class_exists;
use function get_declared_classes;
use Pest\Contracts\HasPrintableTestCaseName;
use Pest\TestCases\IgnorableTestCase;
use Pest\TestSuite;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionException;
use function stripos;
use function strlen;
use function substr;
/**
@ -83,36 +82,43 @@ final class TestSuiteLoader
{
$suiteClassName = $this->classNameFromFileName($suiteClassFile);
if (!class_exists($suiteClassName, false)) {
(static function () use ($suiteClassFile) {
include_once $suiteClassFile;
(static function () use ($suiteClassFile) {
include_once $suiteClassFile;
TestSuite::getInstance()->tests->makeIfNeeded($suiteClassFile);
})();
TestSuite::getInstance()->tests->makeIfNeeded($suiteClassFile);
})();
$loadedClasses = array_values(
array_diff(
get_declared_classes(),
array_merge(
self::$declaredClasses,
self::$loadedClasses
)
$loadedClasses = array_values(
array_diff(
get_declared_classes(),
array_merge(
self::$declaredClasses,
self::$loadedClasses
)
);
)
);
self::$loadedClasses = array_merge($loadedClasses, self::$loadedClasses);
self::$loadedClasses = array_merge($loadedClasses, self::$loadedClasses);
if (empty(self::$loadedClasses)) {
return $this->exceptionFor($suiteClassName, $suiteClassFile);
if (empty(self::$loadedClasses)) {
return $this->exceptionFor($suiteClassName, $suiteClassFile);
}
$testCaseFound = false;
foreach (self::$loadedClasses as $loadedClass) {
if (is_subclass_of($loadedClass, HasPrintableTestCaseName::class)) {
$suiteClassName = $loadedClass;
$testCaseFound = true;
break;
}
}
if (!class_exists($suiteClassName, false)) {
// this block will handle namespaced classes
$offset = 0 - strlen($suiteClassName);
if (!$testCaseFound) {
foreach (self::$loadedClasses as $loadedClass) {
if (stripos(substr($loadedClass, $offset - 1), '\\' . $suiteClassName) === 0) {
if (is_subclass_of($loadedClass, TestCase::class)) {
$suiteClassName = $loadedClass;
break;

View File

@ -1,8 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd"
backupGlobals="false"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
bootstrap="vendor/autoload.php"
cacheResult="false"
colors="true"
failOnRisky="true"
failOnWarning="true"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
cacheDirectory=".phpunit.cache"
backupStaticProperties="false"
displayDetailsOnIncompleteTests="true"
displayDetailsOnSkippedTests="true"
displayDetailsOnTestsThatTriggerDeprecations="true"
displayDetailsOnTestsThatTriggerErrors="true"
displayDetailsOnTestsThatTriggerNotices="true"
displayDetailsOnTestsThatTriggerWarnings="true"
>
<testsuites>
<testsuite name="default">

View File

@ -9,13 +9,21 @@ use Pest\Support\ChainableClosure;
use Pest\Support\ExceptionTrace;
use Pest\Support\Reflection;
use Pest\TestSuite;
use PHPUnit\Framework\TestCase;
use Throwable;
/**
* @internal
*
* @mixin TestCase
*/
trait Testable
{
/**
* Test method description.
*/
private static string $__description;
/**
* The Test Case "test" closure.
*/
@ -118,14 +126,6 @@ trait Testable
: $hook;
}
/**
* Gets the Test Case filename.
*/
public static function __getFilename(): string
{
return self::$__filename;
}
/**
* This method is called before the first test of this Test Case is run.
*/
@ -211,6 +211,13 @@ trait Testable
*/
private function __resolveTestArguments(array $arguments): array
{
$method = TestSuite::getInstance()->tests->get(self::$__filename)->getMethod($this->name());
if ($this->dataName()) {
self::$__description = $method->description . ' with ' . $this->dataName();
} else {
self::$__description = $method->description;
}
if (count($arguments) !== 1) {
return $arguments;
}
@ -246,8 +253,16 @@ trait Testable
/**
* Gets the Test Case name that should be used by printers.
*/
public function getPrintableTestCaseName(): string
public static function getPrintableTestCaseName(): string
{
return ltrim(self::class, 'P\\');
}
/**
* Gets the Test Case name that should be used by printers.
*/
public static function getPrintableTestCaseMethodName(): string
{
return self::$__description;
}
}

View File

@ -12,9 +12,9 @@ interface HandlesArguments
/**
* Adds arguments before of the Test Suite execution.
*
* @param array<int, string> $argv
* @param array<int, string> $arguments
*
* @return array<int, string>
*/
public function handleArguments(array $argv): array;
public function handleArguments(array $arguments): array;
}

View File

@ -19,7 +19,7 @@ final class ShouldNotHappen extends RuntimeException
{
$message = $exception->getMessage();
parent::__construct(sprintf(<<<EOF
parent::__construct(sprintf(<<<'EOF'
This should not happen - please create an new issue here: https://github.com/pestphp/pest.

View File

@ -186,6 +186,7 @@ final class Expectation
foreach ($values as $key => $item) {
if ($callbacks[$key] instanceof Closure) {
call_user_func($callbacks[$key], new self($item), new self($keys[$key]));
continue;
}
@ -220,6 +221,7 @@ final class Expectation
if (is_callable($callback)) {
$callback(new self($this->value));
continue;
}

View File

@ -37,7 +37,7 @@ final class HigherOrderExpectation
*/
public function __construct(private Expectation $original, mixed $value)
{
$this->expectation = $this->expect($value);
$this->expectation = $this->expect($value);
}
/**

View File

@ -121,7 +121,17 @@ final class TestCaseFactory
$filename = str_replace('\\\\', '\\', addslashes((string) realpath($filename)));
$rootPath = TestSuite::getInstance()->rootPath;
$relativePath = str_replace($rootPath . DIRECTORY_SEPARATOR, '', $filename);
$relativePath = dirname(ucfirst($relativePath)) . DIRECTORY_SEPARATOR . basename($relativePath, '.php');
$basename = basename($relativePath, '.php');
$dotPos = strpos($basename, '.');
if ($dotPos !== false) {
$basename = substr($basename, 0, $dotPos);
}
$relativePath = dirname(ucfirst($relativePath)) . DIRECTORY_SEPARATOR . $basename;
$relativePath = str_replace(DIRECTORY_SEPARATOR, '\\', $relativePath);
// Strip out any %-encoded octets.
@ -132,6 +142,7 @@ final class TestCaseFactory
$relativePath = (string) preg_replace('/[^A-Za-z0-9\\\\]/', '', $relativePath);
$classFQN = 'P\\' . $relativePath;
if (class_exists($classFQN)) {
return;
}
@ -174,27 +185,28 @@ final class TestCaseFactory
));
$classAttributesCode = implode('', array_map(
static fn (string $attribute) => sprintf("\n %s", $attribute),
static fn (string $attribute) => sprintf("\n%s", $attribute),
array_unique($classAttributes),
));
try {
eval("
namespace $namespace;
$classCode = <<<PHP
namespace $namespace;
use Pest\Repositories\DatasetsRepository as __PestDatasets;
use Pest\TestSuite as __PestTestSuite;
use Pest\Repositories\DatasetsRepository as __PestDatasets;
use Pest\TestSuite as __PestTestSuite;
$classAttributesCode
#[\AllowDynamicProperties]
final class $className extends $baseClass implements $hasPrintableTestCaseClassFQN {
$traitsCode
$classAttributesCode
#[\AllowDynamicProperties]
final class $className extends $baseClass implements $hasPrintableTestCaseClassFQN {
$traitsCode
private static \$__filename = '$filename';
private static \$__filename = '$filename';
$methodsCode
}
PHP;
$methodsCode
}
");
eval($classCode);
} catch (ParseError $caught) {
throw new RuntimeException(sprintf('Unable to create test case for test file at %s', $filename), 1, $caught);
}

View File

@ -91,7 +91,6 @@ final class TestCaseMethodFactory
return function () use ($testCase, $method, $closure): mixed { // @phpstan-ignore-line
/* @var TestCase $this */
$testCase->proxies->proxy($this);
$method->proxies->proxy($this);
@ -148,28 +147,29 @@ final class TestCaseMethodFactory
}
$annotations = implode('', array_map(
static fn ($annotation) => sprintf("\n * %s", $annotation), $annotations,
static fn ($annotation) => sprintf("\n * %s", $annotation), $annotations,
));
$attributes = implode('', array_map(
static fn ($attribute) => sprintf("\n %s", $attribute), $attributes,
));
return <<<EOF
return <<<PHP
/**$annotations
*/
$attributes
public function $methodName()
{
\$test = \Pest\TestSuite::getInstance()->tests->get(self::\$__filename)->getMethod(\$this->name())->getClosure(\$this);
return \$this->__runTest(
\$this->__test,
\$test,
...func_get_args(),
);
}
$datasetsCode
EOF;
PHP;
}
/**

View File

@ -44,10 +44,10 @@ final class PestDatasetCommand extends Command
/** @var string $name */
$name = $this->argument('name');
$relativePath = sprintf(testDirectory('DatasetsRepository/%s.php'), ucfirst($name));
$relativePath = sprintf(testDirectory('DatasetsRepository/%s.php'), ucfirst($name));
/* @phpstan-ignore-next-line */
$target = base_path($relativePath);
$target = base_path($relativePath);
if (File::exists($target)) {
throw new InvalidConsoleArgument(sprintf('%s already exist', $target));

View File

@ -41,8 +41,8 @@ final class PestInstallCommand extends Command
TestSuite::getInstance(base_path(), $this->option('test-directory'));
/* @phpstan-ignore-next-line */
$pest = base_path(testDirectory('Pest.php'));
$stubs = 'stubs/Laravel';
$pest = base_path(testDirectory('Pest.php'));
$stubs = 'stubs/Laravel';
if (File::exists($pest)) {
throw new InvalidConsoleArgument(sprintf('%s already exist', $pest));

View File

@ -19,6 +19,20 @@ trait HandleArguments
return in_array($argument, $arguments, true);
}
/**
* Adds the given argument and value to the list of arguments.
*
* @param array<int, string> $arguments
*
* @return array<int, string>
*/
public function pushArgument(string $argument, array $arguments): array
{
$arguments[] = $argument;
return $arguments;
}
/**
* Pops the given argument from the arguments.
*

View File

@ -14,7 +14,7 @@ final class Environment implements HandlesArguments
/**
* The continuous integration environment.
*/
public const CI = 'ci';
public const CI = 'ci';
/**
* The local environment.

27
src/Plugins/Printer.php Normal file
View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Pest\Plugins;
use Pest\Contracts\Plugins\HandlesArguments;
/**
* @internal
*/
final class Printer implements HandlesArguments
{
use Concerns\HandleArguments;
/**
* {@inheritDoc}
*/
public function handleArguments(array $arguments): array
{
if (! array_key_exists('COLLISION_PRINTER', $_SERVER)) {
return $arguments;
}
return $this->pushArgument('--no-output', $arguments);
}
}

View File

@ -57,6 +57,11 @@ final class DatasetsRepository
self::$withs[$filename . '>>>' . $description] = $with;
}
public static function has(string $filename, string $description): bool
{
return array_key_exists($filename . '>>>' . $description, self::$withs);
}
/**
* @return Closure|iterable<int|string, mixed>|never
*
@ -107,7 +112,7 @@ final class DatasetsRepository
$values = array_merge($values, $datasetCombinationElement['values']);
}
$datasetDescriptions[] = $description . ' with ' . implode(' / ', $partialDescriptions);
$datasetDescriptions[] = implode(' / ', $partialDescriptions);
$datasetValues[] = $values;
}

View File

@ -56,7 +56,7 @@ final class ExceptionTrace
$cleanedTrace = [];
foreach ($trace as $item) {
if (key_exists('file', $item) && mb_strpos($item['file'], 'vendor/pestphp/pest/') > 0) {
if (array_key_exists('file', $item) && mb_strpos($item['file'], 'vendor/pestphp/pest/') > 0) {
continue;
}

View File

@ -13,6 +13,7 @@ abstract class Printer implements \PHPUnit\Util\Printer
private $stream;
private bool $isPhpStream;
private bool $isOpen;
private function __construct(string $out)

View File

@ -12,11 +12,4 @@ use PHPUnit\Framework\TestCase;
*/
class IgnorableTestCase extends TestCase
{
/**
* @test
*/
public function fake(): void
{
self::markTestIncomplete();
}
}

View File

@ -70,12 +70,12 @@ final class TestSuite
string $rootPath,
public string $testPath)
{
$this->beforeAll = new BeforeAllRepository();
$this->beforeEach = new BeforeEachRepository();
$this->tests = new TestRepository();
$this->afterEach = new AfterEachRepository();
$this->afterAll = new AfterAllRepository();
$this->retryTempRepository = new TempRepository('retry');
$this->beforeAll = new BeforeAllRepository();
$this->beforeEach = new BeforeEachRepository();
$this->tests = new TestRepository();
$this->afterEach = new AfterEachRepository();
$this->afterAll = new AfterAllRepository();
$this->retryTempRepository = new TempRepository('retry');
$this->rootPath = (string) realpath($rootPath);
}

View File

@ -1,7 +1,4 @@
PASS Tests\CustomTestCase\ExecutedTest
✓ that gets executed
PASS Tests\Features\AfterAll
✓ deletes file after all
@ -23,6 +20,15 @@
✓ it adds coverage if --min exist
✓ it generates coverage based on file input
PASS Tests\Features\Covers
✓ it uses the correct PHPUnit attribute for class
✓ it uses the correct PHPUnit attribute for function
✓ it removes duplicated attributes
✓ it guesses if the given argument is a class or function
✓ it appends CoversNothing to method attributes
✓ it does not append CoversNothing to other methods
✓ it throws exception if no class nor method has been found
PASS Tests\Features\Datasets
✓ it throws exception if dataset does not exist
✓ it throws exception if dataset already exist
@ -111,16 +117,35 @@
✓ it can correctly resolve a bound dataset that returns an array with (Closure Object (...))
✓ it can correctly resolve a bound dataset that returns an array but wants to be spread with (Closure Object (...))
PASS Tests\Features\Depends
✓ first
✓ second
✓ depends
✓ depends with ...params
✓ depends with defined arguments
✓ depends run test only once
✓ it asserts true is true
✓ depends works with the correct test name
PASS Tests\Features\DependsInheritance
✓ it is a test
✓ it uses correct parent class
PASS Tests\Features\Exceptions
✓ it gives access the the underlying expectException
✓ it catch exceptions
✓ it catch exceptions and messages
✓ it catch exceptions, messages and code
✓ it can just define the message
✓ it can just define the code
✓ it not catch exceptions if given condition is false
✓ it catch exceptions if given condition is true
✓ it catch exceptions and messages if given condition is true
✓ it catch exceptions, messages and code if given condition is true
✓ it can just define the message if given condition is true
✓ it can just define the code if given condition is true
✓ it can just define the message if given condition is 1
✓ it can just define the code if given condition is 1
PASS Tests\Features\Expect\HigherOrder\methods
✓ it can access methods
@ -133,6 +158,8 @@
✓ it can compose complex expectations
✓ it can handle nested method calls
✓ it works with higher order tests
✓ it can use the scoped method to lock into the given level for expectations
✓ it works consistently with the json expectation method
PASS Tests\Features\Expect\HigherOrder\methodsAndProperties
✓ it can access methods and properties
@ -140,6 +167,7 @@
✓ it works with higher order tests
✓ it can start a new higher order expectation using the and syntax
✓ it can start a new higher order expectation using the and syntax in higher order tests
✓ it can start a new higher order expectation using the and syntax without nesting expectations
PASS Tests\Features\Expect\HigherOrder\properties
✓ it allows properties to be accessed from the value
@ -161,6 +189,7 @@
✓ chained opposite and non-opposite expectations
✓ it can add expectations via "and"
✓ it accepts callables
✓ it passes the key of the current item to callables
PASS Tests\Features\Expect\extend
✓ it macros true is true
@ -186,15 +215,16 @@
PASS Tests\Features\Expect\not
✓ not property calls
PASS Tests\Features\Expect\pipe
PASS Tests\Features\Expect\pipes
✓ pipe is applied and can stop pipeline
✓ interceptor works with negated expectation
✓ pipe works with negated expectation
✓ pipe is run and can let the pipeline keep going
intercept is applied
✓ intercept stops the pipeline
✓ interception is called only when filter is met
✓ intercept can be filtered with a closure
pipe works with negated expectation
✓ interceptor is applied
✓ interceptor stops the pipeline
✓ interceptor is called only when filter is met
✓ interceptor can be filtered with a closure
✓ interceptor can be filter the expected parameter as well
✓ interceptor works with negated expectation
✓ intercept can add new parameters to the expectation
PASS Tests\Features\Expect\ray
@ -405,6 +435,11 @@
✓ not failures with multiple needles (all failing)
✓ not failures with multiple needles (some failing)
PASS Tests\Features\Expect\toContainOnlyInstancesOf
✓ pass
✓ failures
✓ not failures
PASS Tests\Features\Expect\toEndWith
✓ pass
✓ failures
@ -452,8 +487,11 @@
PASS Tests\Features\Expect\toHaveKeys
✓ pass
✓ pass with multi-dimensional arrays
✓ failures
✓ failures with multi-dimensional arrays
✓ not failures
✓ not failures with multi-dimensional arrays
PASS Tests\Features\Expect\toHaveLength
✓ it passes with ('Fortaleza')
@ -496,6 +534,7 @@
PASS Tests\Features\Expect\toMatchObject
✓ pass
✓ pass with class
✓ failures
✓ not failures
@ -516,6 +555,8 @@
✓ not failures
✓ closure missing parameter
✓ closure missing type-hint
✓ it can handle a non-defined exception
✓ it can handle a class not found Error
PASS Tests\Features\Expect\unless
✓ it pass
@ -548,9 +589,9 @@
✓ it is capable doing multiple assertions
✓ it resolves expect callables correctly
✓ does not treat method names as callables
✓ it can tap into the test
✓ it can defer a method until after test setup
✓ it can pass datasets into the expect callables with (1, 2, 3)
✓ it can pass datasets into the tap callable with (1, 2, 3)
✓ it can pass datasets into the defer callable with (1, 2, 3)
✓ it can pass shared datasets into callables with (1)
✓ it can pass shared datasets into callables with (2)
@ -578,7 +619,7 @@
WARN Tests\Features\Skip
✓ it do not skips
- it skips with truthy
- it skips with truthy → 1
- it skips with truthy condition by default
- it skips with message → skipped because bar
- it skips with truthy closure condition
@ -592,6 +633,10 @@
✓ a test
✓ higher order message test
PASS Tests\Features\ThrowsNoExceptions
✓ it allows access to the underlying expectNotToPerformAssertions method
✓ it allows performing no expectations without being risky
PASS Tests\Fixtures\DirectoryWithTests\ExampleTest
✓ it example 1
@ -618,14 +663,14 @@
PASS Tests\PHPUnit\CustomAffixes\ATestWithSpaces
✓ it runs file names like `A Test With Spaces.php`
PASS Tests\PHPUnit\CustomAffixes\AdditionalFileExtensionspec
PASS Tests\PHPUnit\CustomAffixes\AdditionalFileExtension
✓ it runs file names like `AdditionalFileExtension.spec.php`
PASS Tests\PHPUnit\CustomAffixes\FolderWithAn\ExampleTest
✓ custom traits can be used
✓ trait applied in this file
PASS Tests\PHPUnit\CustomAffixes\ManyExtensionsclasstest
PASS Tests\PHPUnit\CustomAffixes\ManyExtensions
✓ it runs file names like `ManyExtensions.class.test.php`
PASS Tests\PHPUnit\CustomAffixes\TestCaseWithQuotes
@ -654,18 +699,14 @@
✓ it allows global uses
✓ it allows multiple global uses registered in the same path
PASS Tests\Unit\Actions\AddsDefaults
✓ it sets defaults
it does not override options
PASS Tests\Unit\Actions\AddsTests
✓ default php unit tests
it removes warnings
PASS Tests\Unit\Actions\ValidatesConfiguration
✓ it throws exception when configuration not found
✓ it throws exception when `process isolation` is true
✓ it do not throws exception when `process isolation` is false
WARN Tests\Unit\ConfigLoader
✓ it fallbacks to default path if no phpunit file is found
- it fallbacks to default path if phpunit is not a valid XML
- it fallbacks to default path if failing to read phpunit content
- it fallbacks to default path if there is no test suites directory
- it fallbacks to default path if test suite directory has no value
- it fallbacks to default path if test suite directory does not exist
- it returns the parent folder of first test suite directory
PASS Tests\Unit\Console\Help
✓ it outputs the help information when --help is used
@ -681,6 +722,9 @@
✓ environment is set to CI when --ci option is used
✓ environment is set to Local when --ci option is not used
PASS Tests\Unit\Plugins\Retry
✓ it retries if --retry argument is used
PASS Tests\Unit\Plugins\Version
✓ it outputs the version when --version is used
✓ it do not outputs version when --version is not used
@ -708,37 +752,66 @@
✓ it can filter the test suite filenames to those with the only method
✓ it does not filter the test suite filenames to those with the only method when working in CI pipeline
PASS Tests\Visual\Help
visual snapshot of help command output
WARN Tests\Visual\Help
- visual snapshot of help command output → Not supported yet.
PASS Tests\Visual\JUnit
it is can successfully call all public methods
WARN Tests\Visual\JUnit
- it is can successfully call all public methods → Not supported yet.
PASS Tests\Visual\SingleTestOrDirectory
FAIL Tests\Visual\SingleTestOrDirectory
✓ allows to run a single test
✓ allows to run a directory
it has ascii chars
it has ascii chars
✓ it disable decorating printer when colors is set to never
WARN Tests\Visual\Success
- visual snapshot of test suite on success
PASS Tests\Visual\TeamCity
it is can successfully call all public methods
WARN Tests\Visual\TeamCity
- it is can successfully call all public methods → Not supported yet.
PASS Tests\Features\Depends
✓ first
✓ second
✓ it asserts true is true
✓ depends
✓ depends with ...params
✓ depends with defined arguments
✓ depends run test only once
✓ depends works with the correct test name
PHPUnit\Framework\ExpectationFailedException
PASS Tests\Features\DependsInheritance
✓ it is a test
✓ it uses correct parent class
Failed asserting that '\n
PASS Tests\Fixtures\DirectoryWithTests\ExampleTest\n
✓ it example 1\n
\n
Tests: 1 passed\n
Time: 0.00s\n
\n
' contains "
PASS Tests\Fixtures\DirectoryWithTests\ExampleTest
✓ it example 1
Tests: 4 incompleted, 9 skipped, 487 passed
Tests: 1 passed
".
/Users/nunomaduro/Work/Code/pest/src/Mixins/Expectation.php:181
/Users/nunomaduro/Work/Code/pest/src/Support/ExpectationPipeline.php:80
/Users/nunomaduro/Work/Code/pest/src/Support/ExpectationPipeline.php:84
/Users/nunomaduro/Work/Code/pest/src/Expectation.php:297
/Users/nunomaduro/Work/Code/pest/tests/Visual/SingleTestOrDirectory.php:32
/Users/nunomaduro/Work/Code/pest/src/Factories/TestCaseMethodFactory.php:101
/Users/nunomaduro/Work/Code/pest/src/Concerns/Testable.php:251
/Users/nunomaduro/Work/Code/pest/src/Support/ExceptionTrace.php:29
/Users/nunomaduro/Work/Code/pest/src/Concerns/Testable.php:251
/Users/nunomaduro/Work/Code/pest/src/Concerns/Testable.php:205
/Users/nunomaduro/Work/Code/pest/src/Kernel.php:57
at src/Mixins/Expectation.php:181
177▕ {
178▕ foreach ($needles as $needle) {
179▕ if (is_string($this->value)) {
180▕ // @phpstan-ignore-next-line
➜ 181▕ Assert::assertStringContainsString((string) $needle, $this->value);
182▕ } else {
183▕ if (!is_iterable($this->value)) {
184▕ InvalidExpectationValue::expected('iterable');
185▕ }
1 src/Mixins/Expectation.php:181
2 src/Support/ExpectationPipeline.php:80
Tests: 1 failed, 4 incompleted, 18 skipped, 514 passed, 8 pending

View File

@ -1,11 +1,19 @@
<?php
dataset('bound.closure', function () {
yield function () { return 1; };
yield function () { return 2; };
yield function () {
return 1;
};
yield function () {
return 2;
};
});
dataset('bound.array', [
function () { return 1; },
function () { return 2; },
function () {
return 1;
},
function () {
return 2;
},
]);

View File

@ -9,7 +9,6 @@ it('has plugin')->assertTrue(class_exists(CoveragePlugin::class));
it('adds coverage if --coverage exist', function () {
$plugin = new CoveragePlugin(new ConsoleOutput());
$testSuite = TestSuite::getInstance();
expect($plugin->coverage)->toBeFalse();
$arguments = $plugin->handleArguments([]);

View File

@ -28,23 +28,23 @@ it('sets closures', function () {
yield [1];
});
expect(DatasetsRepository::resolve('foo', ['foo']))->toBe(['foo with (1)' => [1]]);
expect(DatasetsRepository::resolve('foo', ['foo']))->toBe(['(1)' => [1]]);
});
it('sets arrays', function () {
DatasetsRepository::set('bar', [[2]]);
expect(DatasetsRepository::resolve('bar', ['bar']))->toBe(['bar with (2)' => [2]]);
expect(DatasetsRepository::resolve('bar', ['bar']))->toBe(['(2)' => [2]]);
});
it('gets bound to test case object', function () {
it('gets bound to test case object', function ($value) {
$this->assertTrue(true);
})->with([['a'], ['b']]);
test('it truncates the description', function () {
expect(true)->toBe(true);
// it gets tested by the integration test
})->with([str_repeat('Fooo', 10000000)]);
})->with([str_repeat('Fooo', 10)]);
$state = new stdClass();
$state->text = '';
@ -233,8 +233,12 @@ test('more than two datasets did the job right', function () use ($state) {
it('can resolve a dataset after the test case is available', function ($result) {
expect($result)->toBe('bar');
})->with([
function () { return $this->foo; },
[function () { return $this->foo; }],
function () {
return $this->foo;
},
[function () {
return $this->foo;
}],
]);
it('can resolve a dataset after the test case is available with shared yield sets', function ($result) {
@ -249,38 +253,54 @@ it('resolves a potential bound dataset logically', function ($foo, $bar) {
expect($foo)->toBe('foo');
expect($bar())->toBe('bar');
})->with([
['foo', function () { return 'bar'; }], // This should be passed as a closure because we've passed multiple arguments
['foo', function () {
return 'bar';
}], // This should be passed as a closure because we've passed multiple arguments
]);
it('resolves a potential bound dataset logically even when the closure comes first', function ($foo, $bar) {
expect($foo())->toBe('foo');
expect($bar)->toBe('bar');
})->with([
[function () { return 'foo'; }, 'bar'], // This should be passed as a closure because we've passed multiple arguments
[function () {
return 'foo';
}, 'bar'], // This should be passed as a closure because we've passed multiple arguments
]);
it('will not resolve a closure if it is type hinted as a closure', function (Closure $data) {
expect($data())->toBeString();
})->with([
function () { return 'foo'; },
function () { return 'bar'; },
function () {
return 'foo';
},
function () {
return 'bar';
},
]);
it('will not resolve a closure if it is type hinted as a callable', function (callable $data) {
expect($data())->toBeString();
})->with([
function () { return 'foo'; },
function () { return 'bar'; },
function () {
return 'foo';
},
function () {
return 'bar';
},
]);
it('can correctly resolve a bound dataset that returns an array', function (array $data) {
expect($data)->toBe(['foo', 'bar', 'baz']);
})->with([
function () { return ['foo', 'bar', 'baz']; },
function () {
return ['foo', 'bar', 'baz'];
},
]);
it('can correctly resolve a bound dataset that returns an array but wants to be spread', function (string $foo, string $bar, string $baz) {
expect([$foo, $bar, $baz])->toBe(['foo', 'bar', 'baz']);
})->with([
function () { return ['foo', 'bar', 'baz']; },
function () {
return ['foo', 'bar', 'baz'];
},
]);

View File

@ -32,7 +32,9 @@ it('not catch exceptions if given condition is false', function () {
it('catch exceptions if given condition is true', function () {
throw new Exception('Something bad happened');
})->throwsIf(function () { return true; }, Exception::class);
})->throwsIf(function () {
return true;
}, Exception::class);
it('catch exceptions and messages if given condition is true', function () {
throw new Exception('Something bad happened');

View File

@ -40,8 +40,12 @@ it('works inside of each', function () {
it('works with sequence', function () {
expect(new HasMethods())
->books()->sequence(
function ($book) { $book->title->toEqual('Foo')->cost->toEqual(20); },
function ($book) { $book->title->toEqual('Bar')->cost->toEqual(30); },
function ($book) {
$book->title->toEqual('Foo')->cost->toEqual(20);
},
function ($book) {
$book->title->toEqual('Bar')->cost->toEqual(30);
},
);
});
@ -54,8 +58,12 @@ it('can compose complex expectations', function () {
->attributes()->toBeArray()
->books()->toBeArray->each->not->toBeEmpty
->books()->sequence(
function ($book) { $book->title->toEqual('Foo')->cost->toEqual(20); },
function ($book) { $book->title->toEqual('Bar')->cost->toEqual(30); },
function ($book) {
$book->title->toEqual('Foo')->cost->toEqual(20);
},
function ($book) {
$book->title->toEqual('Bar')->cost->toEqual(30);
},
);
});

View File

@ -9,8 +9,12 @@ it('can access methods and properties', function () {
})->books()->toBeArray()
->posts->toBeArray->each->not->toBeEmpty
->books()->sequence(
function ($book) { $book->title->toEqual('Foo')->cost->toEqual(20); },
function ($book) { $book->title->toEqual('Bar')->cost->toEqual(30); },
function ($book) {
$book->title->toEqual('Foo')->cost->toEqual(20);
},
function ($book) {
$book->title->toEqual('Bar')->cost->toEqual(30);
},
);
});
@ -53,7 +57,9 @@ it('can start a new higher order expectation using the and syntax without nestin
->toBeInstanceOf(HasMethodsAndProperties::class)
->meta
->sequence(
function ($value, $key) { $value->toBeArray()->and($key)->toBe('foo'); },
function ($value, $key) {
$value->toBeArray()->and($key)->toBe('foo');
},
);
});

View File

@ -35,8 +35,12 @@ it('works inside of each', function () {
it('works with sequence', function () {
expect(['books' => [['title' => 'Foo', 'cost' => 20], ['title' => 'Bar', 'cost' => 30]]])
->books->sequence(
function ($book) { $book->title->toEqual('Foo')->cost->toEqual(20); },
function ($book) { $book->title->toEqual('Bar')->cost->toEqual(30); },
function ($book) {
$book->title->toEqual('Foo')->cost->toEqual(20);
},
function ($book) {
$book->title->toEqual('Bar')->cost->toEqual(30);
},
);
});
@ -51,10 +55,16 @@ it('can compose complex expectations', function () {
it('works with objects', function () {
expect(new HasProperties())
->name->toEqual('foo')->not->toEqual('world')
->posts->toHaveCount(2)->each(function ($post) { $post->is_published->toBeTrue(); })
->posts->toHaveCount(2)->each(function ($post) {
$post->is_published->toBeTrue();
})
->posts->sequence(
function ($post) { $post->title->toEqual('Foo'); },
function ($post) { $post->title->toEqual('Bar'); },
function ($post) {
$post->title->toEqual('Foo');
},
function ($post) {
$post->title->toEqual('Bar');
},
);
});

View File

@ -9,17 +9,17 @@ beforeEach(function () {
it('pass', function () {
expect('baz')
->match('foo', [
'bar' => function ($value) {
$this->matched = 'bar';
'bar' => function ($value) {
$this->matched = 'bar';
return $value->toEqual('bar');
},
'foo' => function ($value) {
$this->matched = 'baz';
return $value->toEqual('bar');
},
'foo' => function ($value) {
$this->matched = 'baz';
return $value->toEqual('baz');
},
]
return $value->toEqual('baz');
},
]
)
->toEqual($this->matched);
@ -29,30 +29,30 @@ it('pass', function () {
it('failures', function () {
expect(true)
->match('foo', [
'bar' => function ($value) {
return $value->toBeTrue();
},
'foo' => function ($value) {
return $value->toBeFalse();
},
]
'bar' => function ($value) {
return $value->toBeTrue();
},
'foo' => function ($value) {
return $value->toBeFalse();
},
]
);
})->throws(ExpectationFailedException::class, 'true is false');
it('runs with truthy', function () {
expect('foo')
->match(1, [
'bar' => function ($value) {
$this->matched = 'bar';
'bar' => function ($value) {
$this->matched = 'bar';
return $value->toEqual('bar');
},
true => function ($value) {
$this->matched = 'foo';
return $value->toEqual('bar');
},
true => function ($value) {
$this->matched = 'foo';
return $value->toEqual('foo');
},
]
return $value->toEqual('foo');
},
]
)
->toEqual($this->matched);
@ -62,17 +62,17 @@ it('runs with truthy', function () {
it('runs with falsy', function () {
expect('foo')
->match(false, [
'bar' => function ($value) {
$this->matched = 'bar';
'bar' => function ($value) {
$this->matched = 'bar';
return $value->toEqual('bar');
},
false => function ($value) {
$this->matched = 'foo';
return $value->toEqual('bar');
},
false => function ($value) {
$this->matched = 'foo';
return $value->toEqual('foo');
},
]
return $value->toEqual('foo');
},
]
)
->toEqual($this->matched);
@ -82,7 +82,9 @@ it('runs with falsy', function () {
it('runs with truthy closure condition', function () {
expect('foo')
->match(
function () { return '1'; }, [
function () {
return '1';
}, [
'bar' => function ($value) {
$this->matched = 'bar';
@ -103,7 +105,9 @@ it('runs with truthy closure condition', function () {
it('runs with falsy closure condition', function () {
expect('foo')
->match(
function () { return '0'; }, [
function () {
return '0';
}, [
'bar' => function ($value) {
$this->matched = 'bar';
@ -124,9 +128,9 @@ it('runs with falsy closure condition', function () {
it('can be passed non-callable values', function () {
expect('foo')
->match('pest', [
'bar' => 'foo',
'pest' => 'baz',
]
'bar' => 'foo',
'pest' => 'baz',
]
);
})->throws(ExpectationFailedException::class, 'two strings are equal');
@ -137,7 +141,9 @@ it('fails with unhandled match', function () {
it('can be used in higher order tests')
->expect(true)
->match(
function () { return true; }, [
function () {
return true;
}, [
false => function ($value) {
return $value->toBeFalse();
},

View File

@ -36,7 +36,8 @@ class Symbol
class State
{
public array $runCount = [];
public array $runCount = [];
public array $appliedCount = [];
public function __construct()
@ -132,16 +133,16 @@ test('pipe is applied and can stop pipeline', function () use ($state) {
expect($char)->toBe(new Char('A'))
->and($state)
->runCount->toMatchArray([
'char' => 1,
'number' => 0,
'wildcard' => 0,
'symbol' => 0,
'char' => 1,
'number' => 0,
'wildcard' => 0,
'symbol' => 0,
])
->appliedCount->toMatchArray([
'char' => 1,
'number' => 0,
'wildcard' => 0,
'symbol' => 0,
'char' => 1,
'number' => 0,
'wildcard' => 0,
'symbol' => 0,
]);
});
@ -151,16 +152,16 @@ test('pipe is run and can let the pipeline keep going', function () use ($state)
expect(3)->toBe(3)
->and($state)
->runCount->toMatchArray([
'char' => 1,
'number' => 0,
'wildcard' => 0,
'symbol' => 1,
'char' => 1,
'number' => 0,
'wildcard' => 0,
'symbol' => 1,
])
->appliedCount->toMatchArray([
'char' => 0,
'number' => 0,
'wildcard' => 0,
'symbol' => 0,
'char' => 0,
'number' => 0,
'wildcard' => 0,
'symbol' => 0,
]);
});
@ -172,16 +173,16 @@ test('pipe works with negated expectation', function () use ($state) {
expect($char)->not->toBe(new Char('B'))
->and($state)
->runCount->toMatchArray([
'char' => 1,
'number' => 0,
'wildcard' => 0,
'symbol' => 0,
'char' => 1,
'number' => 0,
'wildcard' => 0,
'symbol' => 0,
])
->appliedCount->toMatchArray([
'char' => 1,
'number' => 0,
'wildcard' => 0,
'symbol' => 0,
'char' => 1,
'number' => 0,
'wildcard' => 0,
'symbol' => 0,
]);
});
@ -204,16 +205,16 @@ test('interceptor stops the pipeline', function () use ($state) {
expect($number)->toBe(new Number(1))
->and($state)
->runCount->toMatchArray([
'char' => 1,
'number' => 1,
'wildcard' => 0,
'symbol' => 0,
'char' => 1,
'number' => 1,
'wildcard' => 0,
'symbol' => 0,
])
->appliedCount->toMatchArray([
'char' => 0,
'number' => 1,
'wildcard' => 0,
'symbol' => 0,
'char' => 0,
'number' => 1,
'wildcard' => 0,
'symbol' => 0,
]);
});

View File

@ -9,9 +9,15 @@ test('an exception is thrown if the the type is not iterable', function () {
test('allows for sequences of checks to be run on iterable data', function () {
expect([1, 2, 3])
->sequence(
function ($expectation) { $expectation->toBeInt()->toEqual(1); },
function ($expectation) { $expectation->toBeInt()->toEqual(2); },
function ($expectation) { $expectation->toBeInt()->toEqual(3); },
function ($expectation) {
$expectation->toBeInt()->toEqual(1);
},
function ($expectation) {
$expectation->toBeInt()->toEqual(2);
},
function ($expectation) {
$expectation->toBeInt()->toEqual(3);
},
);
expect(static::getCount())->toBe(6);
@ -20,9 +26,15 @@ test('allows for sequences of checks to be run on iterable data', function () {
test('loops back to the start if it runs out of sequence items', function () {
expect([1, 2, 3, 1, 2, 3, 1, 2])
->sequence(
function ($expectation) { $expectation->toBeInt()->toEqual(1); },
function ($expectation) { $expectation->toBeInt()->toEqual(2); },
function ($expectation) { $expectation->toBeInt()->toEqual(3); },
function ($expectation) {
$expectation->toBeInt()->toEqual(1);
},
function ($expectation) {
$expectation->toBeInt()->toEqual(2);
},
function ($expectation) {
$expectation->toBeInt()->toEqual(3);
},
);
expect(static::getCount())->toBe(16);
@ -31,9 +43,15 @@ test('loops back to the start if it runs out of sequence items', function () {
test('fails if the number of iterable items is greater than the number of expectations', function () {
expect([1, 2])
->sequence(
function ($expectation) { $expectation->toBeInt()->toEqual(1); },
function ($expectation) { $expectation->toBeInt()->toEqual(2); },
function ($expectation) { $expectation->toBeInt()->toEqual(3); },
function ($expectation) {
$expectation->toBeInt()->toEqual(1);
},
function ($expectation) {
$expectation->toBeInt()->toEqual(2);
},
function ($expectation) {
$expectation->toBeInt()->toEqual(3);
},
);
})->throws(ExpectationFailedException::class);
@ -60,7 +78,9 @@ test('it can be passed non-callable values', function () {
test('it can be passed a mixture of value types', function () {
expect(['foo', 'bar', 'baz'])->sequence(
'foo',
function ($expectation) { $expectation->toEqual('bar')->toBeString(); },
function ($expectation) {
$expectation->toEqual('bar')->toBeString();
},
'baz'
);

View File

@ -3,7 +3,8 @@
use PHPUnit\Framework\ExpectationFailedException;
test('pass', function () {
expect(function () {})->toBeCallable();
expect(function () {
})->toBeCallable();
expect(null)->not->toBeCallable();
});
@ -14,5 +15,7 @@ test('failures', function () {
})->throws(ExpectationFailedException::class);
test('not failures', function () {
expect(function () { return 42; })->not->toBeCallable();
expect(function () {
return 42;
})->not->toBeCallable();
})->throws(ExpectationFailedException::class);

View File

@ -3,10 +3,10 @@
use PHPUnit\Framework\ExpectationFailedException;
$test_array = [
'a' => 1,
'a' => 1,
'b',
'c' => 'world',
'd' => [
'c' => 'world',
'd' => [
'e' => 'hello',
],
'key.with.dots' => false,

View File

@ -19,7 +19,8 @@ test('pass', function () {
test('pass with class', function () {
expect(new class() {
public $name = 'Nuno';
public $name = 'Nuno';
public $email = 'enunomaduro@gmail.com';
})->toMatchObject([
'name' => 'Nuno',

View File

@ -3,60 +3,98 @@
use PHPUnit\Framework\ExpectationFailedException;
test('passes', function () {
expect(function () { throw new RuntimeException(); })->toThrow(RuntimeException::class);
expect(function () { throw new RuntimeException(); })->toThrow(Exception::class);
expect(function () { throw new RuntimeException(); })->toThrow(function (RuntimeException $e) {});
expect(function () { throw new RuntimeException('actual message'); })->toThrow(function (Exception $e) {
expect(function () {
throw new RuntimeException();
})->toThrow(RuntimeException::class);
expect(function () {
throw new RuntimeException();
})->toThrow(Exception::class);
expect(function () {
throw new RuntimeException();
})->toThrow(function (RuntimeException $e) {
});
expect(function () {
throw new RuntimeException('actual message');
})->toThrow(function (Exception $e) {
expect($e->getMessage())->toBe('actual message');
});
expect(function () {})->not->toThrow(Exception::class);
expect(function () { throw new RuntimeException('actual message'); })->toThrow('actual message');
expect(function () { throw new Exception(); })->not->toThrow(RuntimeException::class);
expect(function () { throw new RuntimeException('actual message'); })->toThrow(RuntimeException::class, 'actual message');
expect(function () { throw new RuntimeException('actual message'); })->toThrow(function (RuntimeException $e) {}, 'actual message');
expect(function () {
})->not->toThrow(Exception::class);
expect(function () {
throw new RuntimeException('actual message');
})->toThrow('actual message');
expect(function () {
throw new Exception();
})->not->toThrow(RuntimeException::class);
expect(function () {
throw new RuntimeException('actual message');
})->toThrow(RuntimeException::class, 'actual message');
expect(function () {
throw new RuntimeException('actual message');
})->toThrow(function (RuntimeException $e) {
}, 'actual message');
});
test('failures 1', function () {
expect(function () {})->toThrow(RuntimeException::class);
expect(function () {
})->toThrow(RuntimeException::class);
})->throws(ExpectationFailedException::class, 'Exception "' . RuntimeException::class . '" not thrown.');
test('failures 2', function () {
expect(function () {})->toThrow(function (RuntimeException $e) {});
expect(function () {
})->toThrow(function (RuntimeException $e) {
});
})->throws(ExpectationFailedException::class, 'Exception "' . RuntimeException::class . '" not thrown.');
test('failures 3', function () {
expect(function () { throw new Exception(); })->toThrow(function (RuntimeException $e) {});
expect(function () {
throw new Exception();
})->toThrow(function (RuntimeException $e) {
});
})->throws(ExpectationFailedException::class, 'Failed asserting that Exception Object');
test('failures 4', function () {
expect(function () { throw new Exception('actual message'); })
expect(function () {
throw new Exception('actual message');
})
->toThrow(function (Exception $e) {
expect($e->getMessage())->toBe('expected message');
});
})->throws(ExpectationFailedException::class, 'Failed asserting that two strings are identical');
test('failures 5', function () {
expect(function () { throw new Exception('actual message'); })->toThrow('expected message');
expect(function () {
throw new Exception('actual message');
})->toThrow('expected message');
})->throws(ExpectationFailedException::class, 'Failed asserting that \'actual message\' contains "expected message".');
test('failures 6', function () {
expect(function () {})->toThrow('actual message');
expect(function () {
})->toThrow('actual message');
})->throws(ExpectationFailedException::class, 'Exception with message "actual message" not thrown');
test('failures 7', function () {
expect(function () { throw new RuntimeException('actual message'); })->toThrow(RuntimeException::class, 'expected message');
expect(function () {
throw new RuntimeException('actual message');
})->toThrow(RuntimeException::class, 'expected message');
})->throws(ExpectationFailedException::class);
test('not failures', function () {
expect(function () { throw new RuntimeException(); })->not->toThrow(RuntimeException::class);
expect(function () {
throw new RuntimeException();
})->not->toThrow(RuntimeException::class);
})->throws(ExpectationFailedException::class);
test('closure missing parameter', function () {
expect(function () {})->toThrow(function () {});
expect(function () {
})->toThrow(function () {
});
})->throws(InvalidArgumentException::class, 'The given closure must have a single parameter type-hinted as the class string.');
test('closure missing type-hint', function () {
expect(function () {})->toThrow(function ($e) {});
expect(function () {
})->toThrow(function ($e) {
});
})->throws(InvalidArgumentException::class, 'The given closure\'s parameter must be type-hinted as the class string.');
it('can handle a non-defined exception', function () {

View File

@ -67,7 +67,9 @@ it('skips with falsy', function () {
it('runs with truthy closure condition', function () {
expect($this->unlessObject)
->unless(
function () { return '0'; },
function () {
return '0';
},
function ($value) {
return $value->trueValue->toBeTrue();
}
@ -80,7 +82,9 @@ it('runs with truthy closure condition', function () {
it('skips with falsy closure condition', function () {
expect($this->unlessObject)
->unless(
function () { return '1'; },
function () {
return '1';
},
function ($value) {
return $value->trueValue->toBeFalse(); // fails
}
@ -93,7 +97,9 @@ it('skips with falsy closure condition', function () {
it('can be used in higher order tests')
->expect(true)
->unless(
function () { return false; },
function () {
return false;
},
function ($value) {
return $value->toBeFalse();
}

View File

@ -67,7 +67,9 @@ it('skips with falsy', function () {
it('runs with truthy closure condition', function () {
expect($this->whenObject)
->when(
function () { return '1'; },
function () {
return '1';
},
function ($value) {
return $value->trueValue->toBeTrue();
}
@ -80,7 +82,9 @@ it('runs with truthy closure condition', function () {
it('skips with falsy closure condition', function () {
expect($this->whenObject)
->when(
function () { return '0'; },
function () {
return '0';
},
function ($value) {
return $value->trueValue->toBeFalse(); // fails
}
@ -93,7 +97,9 @@ it('skips with falsy closure condition', function () {
it('can be used in higher order tests')
->expect(false)
->when(
function () { return true; },
function () {
return true;
},
function ($value) {
return $value->toBeTrue();
}

View File

@ -11,7 +11,9 @@ it('is capable doing multiple assertions')
->assertFalse(false);
it('resolves expect callables correctly')
->expect(function () { return 'foo'; })
->expect(function () {
return 'foo';
})
->toBeString()
->toBe('foo')
->and('bar')
@ -23,24 +25,38 @@ test('does not treat method names as callables')
it('can defer a method until after test setup')
->expect('foo')->toBeString()
->defer(function () { expect($this)->toBeInstanceOf(TestCase::class); })
->defer(function () {
expect($this)->toBeInstanceOf(TestCase::class);
})
->toBe('foo')
->and('hello world')->toBeString();
it('can pass datasets into the expect callables')
->with([[1, 2, 3]])
->expect(function (...$numbers) { return $numbers; })->toBe([1, 2, 3])
->and(function (...$numbers) { return $numbers; })->toBe([1, 2, 3]);
->expect(function (...$numbers) {
return $numbers;
})->toBe([1, 2, 3])
->and(function (...$numbers) {
return $numbers;
})->toBe([1, 2, 3]);
it('can pass datasets into the defer callable')
->with([[1, 2, 3]])
->defer(function (...$numbers) { expect($numbers)->toBe([1, 2, 3]); });
->defer(function (...$numbers) {
expect($numbers)->toBe([1, 2, 3]);
});
it('can pass shared datasets into callables')
->with('numbers.closure.wrapped')
->expect(function ($value) { return $value; })
->and(function ($value) { return $value; })
->defer(function ($value) { expect($value)->toBeInt(); })
->expect(function ($value) {
return $value;
})
->and(function ($value) {
return $value;
})
->defer(function ($value) {
expect($value)->toBeInt();
})
->toBeInt();
afterEach()->assertTrue(true);

View File

@ -21,11 +21,15 @@ it('skips with message')
->assertTrue(false);
it('skips with truthy closure condition')
->skip(function () { return '1'; })
->skip(function () {
return '1';
})
->assertTrue(false);
it('do not skips with falsy closure condition')
->skip(function () { return false; })
->skip(function () {
return false;
})
->assertTrue(true);
it('skips with condition and message')
@ -37,10 +41,16 @@ it('skips when skip after assertion')
->skip();
it('can use something in the test case as a condition')
->skip(function () { return $this->shouldSkip; }, 'This test was skipped')
->skip(function () {
return $this->shouldSkip;
}, 'This test was skipped')
->assertTrue(false);
it('can user higher order callables and skip')
->skip(function () { return $this->shouldSkip; })
->expect(function () { return $this->shouldSkip; })
->skip(function () {
return $this->shouldSkip;
})
->expect(function () {
return $this->shouldSkip;
})
->toBeFalse();

View File

@ -10,8 +10,8 @@ it('show only the names of named datasets in their description', function () {
],
]));
expect($descriptions[0])->toBe('test description with data set "one"');
expect($descriptions[1])->toBe('test description with data set "two"');
expect($descriptions[0])->toBe('data set "one"')
->and($descriptions[1])->toBe('data set "two"');
});
it('show the actual dataset of non-named datasets in their description', function () {
@ -22,8 +22,8 @@ it('show the actual dataset of non-named datasets in their description', functio
],
]));
expect($descriptions[0])->toBe('test description with (1)');
expect($descriptions[1])->toBe('test description with (array(2))');
expect($descriptions[0])->toBe('(1)');
expect($descriptions[1])->toBe('(array(2))');
});
it('show only the names of multiple named datasets in their description', function () {
@ -38,10 +38,10 @@ it('show only the names of multiple named datasets in their description', functi
],
]));
expect($descriptions[0])->toBe('test description with data set "one" / data set "three"');
expect($descriptions[1])->toBe('test description with data set "one" / data set "four"');
expect($descriptions[2])->toBe('test description with data set "two" / data set "three"');
expect($descriptions[3])->toBe('test description with data set "two" / data set "four"');
expect($descriptions[0])->toBe('data set "one" / data set "three"');
expect($descriptions[1])->toBe('data set "one" / data set "four"');
expect($descriptions[2])->toBe('data set "two" / data set "three"');
expect($descriptions[3])->toBe('data set "two" / data set "four"');
});
it('show the actual dataset of multiple non-named datasets in their description', function () {
@ -56,10 +56,10 @@ it('show the actual dataset of multiple non-named datasets in their description'
],
]));
expect($descriptions[0])->toBe('test description with (1) / (3)');
expect($descriptions[1])->toBe('test description with (1) / (array(4))');
expect($descriptions[2])->toBe('test description with (array(2)) / (3)');
expect($descriptions[3])->toBe('test description with (array(2)) / (array(4))');
expect($descriptions[0])->toBe('(1) / (3)');
expect($descriptions[1])->toBe('(1) / (array(4))');
expect($descriptions[2])->toBe('(array(2)) / (3)');
expect($descriptions[3])->toBe('(array(2)) / (array(4))');
});
it('show the correct description for mixed named and not-named datasets', function () {
@ -74,8 +74,8 @@ it('show the correct description for mixed named and not-named datasets', functi
],
]));
expect($descriptions[0])->toBe('test description with data set "one" / (3)');
expect($descriptions[1])->toBe('test description with data set "one" / data set "four"');
expect($descriptions[2])->toBe('test description with (array(2)) / (3)');
expect($descriptions[3])->toBe('test description with (array(2)) / data set "four"');
expect($descriptions[0])->toBe('data set "one" / (3)');
expect($descriptions[1])->toBe('data set "one" / data set "four"');
expect($descriptions[2])->toBe('(array(2)) / (3)');
expect($descriptions[3])->toBe('(array(2)) / data set "four"');
});

View File

@ -20,7 +20,8 @@ it('does not allow to add the same test description twice', function () {
it('alerts users about tests with arguments but no input', function () {
$testSuite = new TestSuite(getcwd(), 'tests');
$method = new TestCaseMethodFactory('foo', 'bar', function (int $arg) {});
$method = new TestCaseMethodFactory('foo', 'bar', function (int $arg) {
});
$testSuite->tests->set($method);
})->throws(
@ -29,7 +30,7 @@ it('alerts users about tests with arguments but no input', function () {
);
it('can return an array of all test suite filenames', function () {
$testSuite = TestSuite::getInstance(getcwd(), 'tests');
$testSuite = new TestSuite(getcwd(), 'tests');
$testSuite->tests->set(new TestCaseMethodFactory('a', 'b', null));
$testSuite->tests->set(new TestCaseMethodFactory('c', 'd', null));
@ -57,9 +58,10 @@ it('can filter the test suite filenames to those with the only method', function
it('does not filter the test suite filenames to those with the only method when working in CI pipeline', function () {
$previousEnvironment = Environment::name();
Environment::name(Environment::CI);
$testSuite = TestSuite::getInstance(getcwd(), 'tests');
$testSuite = new TestSuite(getcwd(), 'tests');
$test = function () {};
$test = function () {
};
$testWithOnly = new TestCaseMethodFactory('a', 'b', null);
$testWithOnly->only = true;

View File

@ -24,4 +24,4 @@ test('visual snapshot of help command output', function () {
};
expect($output())->toContain(file_get_contents($snapshot));
})->skip(PHP_OS_FAMILY === 'Windows');
})->skip(PHP_OS_FAMILY === 'Windows')->skip('Not supported yet.');

View File

@ -22,7 +22,7 @@ it('is can successfully call all public methods', function () {
$junit->endTest($this, 0);
$junit->endTestSuite(new TestSuite());
$this->expectNotToPerformAssertions();
});
})->skip('Not supported yet.');
afterEach(function () {
unlink(__DIR__ . '/junit.html');

View File

@ -9,7 +9,11 @@ test('visual snapshot of test suite on success', function () {
]);
$output = function () use ($testsPath) {
$process = (new Symfony\Component\Process\Process(['php', 'bin/pest'], dirname($testsPath), ['EXCLUDE' => 'integration', 'REBUILD_SNAPSHOTS' => false, 'PARATEST' => 0]));
$process = (new Symfony\Component\Process\Process(
['php', 'bin/pest'],
dirname($testsPath),
['EXCLUDE' => 'integration', 'REBUILD_SNAPSHOTS' => false, 'PARATEST' => 0, 'COLLISION_PRINTER' => 'DefaultPrinter'],
));
$process->run();

View File

@ -25,7 +25,7 @@ it('is can successfully call all public methods', function () {
$teamCity->endTest($this, 0);
$teamCity->printResult(new TestResult());
$teamCity->endTestSuite(new TestSuite());
});
})->skip('Not supported yet.');
afterEach(function () {
unlink(__DIR__ . '/output.txt');