From cdd67a690004c9cbcb1d2ef9e1fbc67cdb10d5c3 Mon Sep 17 00:00:00 2001 From: Fabio Ivona Date: Fri, 26 Nov 2021 15:20:35 +0100 Subject: [PATCH] merge from master --- src/CoreExpectation.php | 81 ++- src/Expectation.php | 799 +++------------------------- src/Support/ExpectationPipeline.php | 25 +- 3 files changed, 132 insertions(+), 773 deletions(-) diff --git a/src/CoreExpectation.php b/src/CoreExpectation.php index cd4a9099..bb7c229e 100644 --- a/src/CoreExpectation.php +++ b/src/CoreExpectation.php @@ -8,6 +8,7 @@ use BadMethodCallException; use Closure; use InvalidArgumentException; use Pest\Concerns\RetrievesValues; +use Pest\Exceptions\InvalidExpectationValue; use Pest\Support\Arr; use Pest\Support\NullClosure; use PHPUnit\Framework\Assert; @@ -29,30 +30,22 @@ final class CoreExpectation { use RetrievesValues; - /** - * The expectation value. - * - * @readonly - * - * @var TValue - */ - public mixed $value; - /** * The exporter instance, if any. * * @readonly */ - private Exporter|null $exporter; + private Exporter|null $exporter = null; /** * Creates a new expectation. * * @param TValue $value */ - public function __construct(mixed $value) - { - $this->value = $value; + public function __construct( + public mixed $value + ) { + // .. } /** @@ -164,8 +157,12 @@ final class CoreExpectation { foreach ($needles as $needle) { if (is_string($this->value)) { - Assert::assertStringContainsString($needle, $this->value); + // @phpstan-ignore-next-line + Assert::assertStringContainsString((string) $needle, $this->value); } else { + if (!is_iterable($this->value)) { + InvalidExpectationValue::expected('iterable'); + } Assert::assertContains($needle, $this->value); } } @@ -180,6 +177,10 @@ final class CoreExpectation */ public function toStartWith(string $expected): CoreExpectation { + if (!is_string($this->value)) { + InvalidExpectationValue::expected('string'); + } + Assert::assertStringStartsWith($expected, $this->value); return $this; @@ -192,6 +193,10 @@ final class CoreExpectation */ public function toEndWith(string $expected): CoreExpectation { + if (!is_string($this->value)) { + InvalidExpectationValue::expected('string'); + } + Assert::assertStringEndsWith($expected, $this->value); return $this; @@ -224,7 +229,7 @@ final class CoreExpectation return $this; } - throw new BadMethodCallException('CoreExpectation value length is not countable.'); + throw new BadMethodCallException('Expectation value length is not countable.'); } /** @@ -232,6 +237,10 @@ final class CoreExpectation */ public function toHaveCount(int $count): CoreExpectation { + if (!is_countable($this->value) && !is_iterable($this->value)) { + InvalidExpectationValue::expected('string'); + } + Assert::assertCount($count, $this->value); return $this; @@ -244,6 +253,7 @@ final class CoreExpectation { $this->toBeObject(); + //@phpstan-ignore-next-line Assert::assertTrue(property_exists($this->value, $name)); if (func_num_args() > 1) { @@ -455,6 +465,8 @@ final class CoreExpectation public function toBeJson(): CoreExpectation { Assert::assertIsString($this->value); + + //@phpstan-ignore-next-line Assert::assertJson($this->value); return $this; @@ -493,6 +505,8 @@ final class CoreExpectation try { Assert::assertTrue(Arr::has($array, $key)); + + /* @phpstan-ignore-next-line */ } catch (ExpectationFailedException $exception) { throw new ExpectationFailedException("Failed asserting that an array has the key '$key'", $exception->getComparisonFailure()); } @@ -523,6 +537,10 @@ final class CoreExpectation */ public function toBeDirectory(): CoreExpectation { + if (!is_string($this->value)) { + InvalidExpectationValue::expected('string'); + } + Assert::assertDirectoryExists($this->value); return $this; @@ -533,6 +551,10 @@ final class CoreExpectation */ public function toBeReadableDirectory(): CoreExpectation { + if (!is_string($this->value)) { + InvalidExpectationValue::expected('string'); + } + Assert::assertDirectoryIsReadable($this->value); return $this; @@ -543,6 +565,10 @@ final class CoreExpectation */ public function toBeWritableDirectory(): CoreExpectation { + if (!is_string($this->value)) { + InvalidExpectationValue::expected('string'); + } + Assert::assertDirectoryIsWritable($this->value); return $this; @@ -553,6 +579,10 @@ final class CoreExpectation */ public function toBeFile(): CoreExpectation { + if (!is_string($this->value)) { + InvalidExpectationValue::expected('string'); + } + Assert::assertFileExists($this->value); return $this; @@ -563,6 +593,10 @@ final class CoreExpectation */ public function toBeReadableFile(): CoreExpectation { + if (!is_string($this->value)) { + InvalidExpectationValue::expected('string'); + } + Assert::assertFileIsReadable($this->value); return $this; @@ -573,6 +607,9 @@ final class CoreExpectation */ public function toBeWritableFile(): CoreExpectation { + if (!is_string($this->value)) { + InvalidExpectationValue::expected('string'); + } Assert::assertFileIsWritable($this->value); return $this; @@ -581,7 +618,7 @@ final class CoreExpectation /** * Asserts that the value array matches the given array subset. * - * @phpstan-param iterable $array + * @param iterable $array */ public function toMatchArray(iterable|object $array): CoreExpectation { @@ -612,11 +649,15 @@ final class CoreExpectation * Asserts that the value object matches a subset * of the properties of an given object. * - * @phpstan-param iterable|object $object + * @param iterable|object $object */ public function toMatchObject(iterable|object $object): CoreExpectation { foreach ((array) $object as $property => $value) { + if (!is_object($this->value) && !is_string($this->value)) { + InvalidExpectationValue::expected('object|string'); + } + Assert::assertTrue(property_exists($this->value, $property)); /* @phpstan-ignore-next-line */ @@ -640,6 +681,9 @@ final class CoreExpectation */ public function toMatch(string $expression): CoreExpectation { + if (!is_string($this->value)) { + InvalidExpectationValue::expected('string'); + } Assert::assertMatchesRegularExpression($expression, $this->value); return $this; @@ -650,7 +694,6 @@ final class CoreExpectation */ public function toMatchConstraint(Constraint $constraint): CoreExpectation { - Assert::assertThat($this->value, $constraint); return $this; @@ -659,7 +702,7 @@ final class CoreExpectation /** * Asserts that executing value throws an exception. * - * @phpstan-param (Closure(Throwable): mixed)|string $exception + * @param (Closure(Throwable): mixed)|string $exception */ public function toThrow(callable|string $exception, string $exceptionMessage = null): CoreExpectation { diff --git a/src/Expectation.php b/src/Expectation.php index 54ad80e2..3310451c 100644 --- a/src/Expectation.php +++ b/src/Expectation.php @@ -6,19 +6,13 @@ namespace Pest; use BadMethodCallException; use Closure; -use InvalidArgumentException; use Pest\Concerns\Extendable; use Pest\Concerns\RetrievesValues; use Pest\Exceptions\InvalidExpectationValue; -use Pest\Support\Arr; -use Pest\Support\NullClosure; +use Pest\Exceptions\PipeException; +use Pest\Support\ExpectationPipeline; use PHPUnit\Framework\Assert; -use PHPUnit\Framework\Constraint\Constraint; use PHPUnit\Framework\ExpectationFailedException; -use ReflectionFunction; -use ReflectionNamedType; -use SebastianBergmann\Exporter\Exporter; -use Throwable; /** * @internal @@ -27,6 +21,8 @@ use Throwable; * * @property Expectation $not Creates the opposite expectation. * @property Each $each Creates an expectation on each element on the traversable value. + * + * @mixin CoreExpectation */ final class Expectation { @@ -34,22 +30,16 @@ final class Expectation __call as __extendsCall; } - /** - * The exporter instance, if any. - * - * @readonly - */ - private ?Exporter $exporter = null; + private CoreExpectation $coreExpectation; /** * Creates a new expectation. * * @param TValue $value */ - public function __construct( - public mixed $value - ) { - // .. + public function __construct(mixed $value) + { + $this->coreExpectation = new CoreExpectation($value); } /** @@ -251,749 +241,80 @@ final class Expectation return $this; } - /** - * Asserts that two variables have the same type and - * value. Used on objects, it asserts that two - * variables reference the same object. - */ - public function toBe(mixed $expected): Expectation - { - Assert::assertSame($expected, $this->value); - - return $this; - } - - /** - * Asserts that the value is empty. - */ - public function toBeEmpty(): Expectation - { - Assert::assertEmpty($this->value); - - return $this; - } - - /** - * Asserts that the value is true. - */ - public function toBeTrue(): Expectation - { - Assert::assertTrue($this->value); - - return $this; - } - - /** - * Asserts that the value is truthy. - */ - public function toBeTruthy(): Expectation - { - Assert::assertTrue((bool) $this->value); - - return $this; - } - - /** - * Asserts that the value is false. - */ - public function toBeFalse(): Expectation - { - Assert::assertFalse($this->value); - - return $this; - } - - /** - * Asserts that the value is falsy. - */ - public function toBeFalsy(): Expectation - { - Assert::assertFalse((bool) $this->value); - - return $this; - } - - /** - * Asserts that the value is greater than $expected. - */ - public function toBeGreaterThan(int|float $expected): Expectation - { - Assert::assertGreaterThan($expected, $this->value); - - return $this; - } - - /** - * Asserts that the value is greater than or equal to $expected. - */ - public function toBeGreaterThanOrEqual(int|float $expected): Expectation - { - Assert::assertGreaterThanOrEqual($expected, $this->value); - - return $this; - } - - /** - * Asserts that the value is less than or equal to $expected. - */ - public function toBeLessThan(int|float $expected): Expectation - { - Assert::assertLessThan($expected, $this->value); - - return $this; - } - - /** - * Asserts that the value is less than $expected. - */ - public function toBeLessThanOrEqual(int|float $expected): Expectation - { - Assert::assertLessThanOrEqual($expected, $this->value); - - return $this; - } - - /** - * Asserts that $needle is an element of the value. - */ - public function toContain(mixed ...$needles): Expectation - { - foreach ($needles as $needle) { - if (is_string($this->value)) { - // @phpstan-ignore-next-line - Assert::assertStringContainsString((string) $needle, $this->value); - } else { - if (!is_iterable($this->value)) { - InvalidExpectationValue::expected('iterable'); - } - Assert::assertContains($needle, $this->value); - } - } - - return $this; - } - - /** - * Asserts that the value starts with $expected. - * - * @param non-empty-string $expected - */ - public function toStartWith(string $expected): Expectation - { - if (!is_string($this->value)) { - InvalidExpectationValue::expected('string'); - } - - Assert::assertStringStartsWith($expected, $this->value); - - return $this; - } - - /** - * Asserts that the value ends with $expected. - * - * @param non-empty-string $expected - */ - public function toEndWith(string $expected): Expectation - { - if (!is_string($this->value)) { - InvalidExpectationValue::expected('string'); - } - - Assert::assertStringEndsWith($expected, $this->value); - - return $this; - } - - /** - * Asserts that $number matches value's Length. - */ - public function toHaveLength(int $number): Expectation - { - if (is_string($this->value)) { - Assert::assertEquals($number, mb_strlen($this->value)); - - return $this; - } - - if (is_iterable($this->value)) { - return $this->toHaveCount($number); - } - - if (is_object($this->value)) { - if (method_exists($this->value, 'toArray')) { - $array = $this->value->toArray(); - } else { - $array = (array) $this->value; - } - - Assert::assertCount($number, $array); - - return $this; - } - - throw new BadMethodCallException('Expectation value length is not countable.'); - } - - /** - * Asserts that $count matches the number of elements of the value. - */ - public function toHaveCount(int $count): Expectation - { - if (!is_countable($this->value) && !is_iterable($this->value)) { - InvalidExpectationValue::expected('string'); - } - - Assert::assertCount($count, $this->value); - - return $this; - } - - /** - * Asserts that the value contains the property $name. - */ - public function toHaveProperty(string $name, mixed $value = null): Expectation - { - $this->toBeObject(); - - //@phpstan-ignore-next-line - Assert::assertTrue(property_exists($this->value, $name)); - - if (func_num_args() > 1) { - /* @phpstan-ignore-next-line */ - Assert::assertEquals($value, $this->value->{$name}); - } - - return $this; - } - - /** - * Asserts that the value contains the provided properties $names. - * - * @param iterable $names - */ - public function toHaveProperties(iterable $names): Expectation - { - foreach ($names as $name) { - $this->toHaveProperty($name); - } - - return $this; - } - - /** - * Asserts that two variables have the same value. - */ - public function toEqual(mixed $expected): Expectation - { - Assert::assertEquals($expected, $this->value); - - return $this; - } - - /** - * Asserts that two variables have the same value. - * The contents of $expected and the $this->value are - * canonicalized before they are compared. For instance, when the two - * variables $expected and $this->value are arrays, then these arrays - * are sorted before they are compared. When $expected and $this->value - * are objects, each object is converted to an array containing all - * private, protected and public attributes. - */ - public function toEqualCanonicalizing(mixed $expected): Expectation - { - Assert::assertEqualsCanonicalizing($expected, $this->value); - - return $this; - } - - /** - * Asserts that the absolute difference between the value and $expected - * is lower than $delta. - */ - public function toEqualWithDelta(mixed $expected, float $delta): Expectation - { - Assert::assertEqualsWithDelta($expected, $this->value, $delta); - - return $this; - } - - /** - * Asserts that the value is one of the given values. - * - * @param iterable $values - */ - public function toBeIn(iterable $values): Expectation - { - Assert::assertContains($this->value, $values); - - return $this; - } - - /** - * Asserts that the value is infinite. - */ - public function toBeInfinite(): Expectation - { - Assert::assertInfinite($this->value); - - return $this; - } - - /** - * Asserts that the value is an instance of $class. - * - * @param class-string $class - */ - public function toBeInstanceOf(string $class): Expectation - { - Assert::assertInstanceOf($class, $this->value); - - return $this; - } - - /** - * Asserts that the value is an array. - */ - public function toBeArray(): Expectation - { - Assert::assertIsArray($this->value); - - return $this; - } - - /** - * Asserts that the value is of type bool. - */ - public function toBeBool(): Expectation - { - Assert::assertIsBool($this->value); - - return $this; - } - - /** - * Asserts that the value is of type callable. - */ - public function toBeCallable(): Expectation - { - Assert::assertIsCallable($this->value); - - return $this; - } - - /** - * Asserts that the value is of type float. - */ - public function toBeFloat(): Expectation - { - Assert::assertIsFloat($this->value); - - return $this; - } - - /** - * Asserts that the value is of type int. - */ - public function toBeInt(): Expectation - { - Assert::assertIsInt($this->value); - - return $this; - } - - /** - * Asserts that the value is of type iterable. - */ - public function toBeIterable(): Expectation - { - Assert::assertIsIterable($this->value); - - return $this; - } - - /** - * Asserts that the value is of type numeric. - */ - public function toBeNumeric(): Expectation - { - Assert::assertIsNumeric($this->value); - - return $this; - } - - /** - * Asserts that the value is of type object. - */ - public function toBeObject(): Expectation - { - Assert::assertIsObject($this->value); - - return $this; - } - - /** - * Asserts that the value is of type resource. - */ - public function toBeResource(): Expectation - { - Assert::assertIsResource($this->value); - - return $this; - } - - /** - * Asserts that the value is of type scalar. - */ - public function toBeScalar(): Expectation - { - Assert::assertIsScalar($this->value); - - return $this; - } - - /** - * Asserts that the value is of type string. - */ - public function toBeString(): Expectation - { - Assert::assertIsString($this->value); - - return $this; - } - - /** - * Asserts that the value is a JSON string. - */ - public function toBeJson(): Expectation - { - Assert::assertIsString($this->value); - - //@phpstan-ignore-next-line - Assert::assertJson($this->value); - - return $this; - } - - /** - * Asserts that the value is NAN. - */ - public function toBeNan(): Expectation - { - Assert::assertNan($this->value); - - return $this; - } - - /** - * Asserts that the value is null. - */ - public function toBeNull(): Expectation - { - Assert::assertNull($this->value); - - return $this; - } - - /** - * Asserts that the value array has the provided $key. - */ - public function toHaveKey(string|int $key, mixed $value = null): Expectation - { - if (is_object($this->value) && method_exists($this->value, 'toArray')) { - $array = $this->value->toArray(); - } else { - $array = (array) $this->value; - } - - try { - Assert::assertTrue(Arr::has($array, $key)); - - /* @phpstan-ignore-next-line */ - } catch (ExpectationFailedException $exception) { - throw new ExpectationFailedException("Failed asserting that an array has the key '$key'", $exception->getComparisonFailure()); - } - - if (func_num_args() > 1) { - Assert::assertEquals($value, Arr::get($array, $key)); - } - - return $this; - } - - /** - * Asserts that the value array has the provided $keys. - * - * @param array $keys - */ - public function toHaveKeys(array $keys): Expectation - { - foreach ($keys as $key) { - $this->toHaveKey($key); - } - - return $this; - } - - /** - * Asserts that the value is a directory. - */ - public function toBeDirectory(): Expectation - { - if (!is_string($this->value)) { - InvalidExpectationValue::expected('string'); - } - - Assert::assertDirectoryExists($this->value); - - return $this; - } - - /** - * Asserts that the value is a directory and is readable. - */ - public function toBeReadableDirectory(): Expectation - { - if (!is_string($this->value)) { - InvalidExpectationValue::expected('string'); - } - - Assert::assertDirectoryIsReadable($this->value); - - return $this; - } - - /** - * Asserts that the value is a directory and is writable. - */ - public function toBeWritableDirectory(): Expectation - { - if (!is_string($this->value)) { - InvalidExpectationValue::expected('string'); - } - - Assert::assertDirectoryIsWritable($this->value); - - return $this; - } - - /** - * Asserts that the value is a file. - */ - public function toBeFile(): Expectation - { - if (!is_string($this->value)) { - InvalidExpectationValue::expected('string'); - } - - Assert::assertFileExists($this->value); - - return $this; - } - - /** - * Asserts that the value is a file and is readable. - */ - public function toBeReadableFile(): Expectation - { - if (!is_string($this->value)) { - InvalidExpectationValue::expected('string'); - } - - Assert::assertFileIsReadable($this->value); - - return $this; - } - - /** - * Asserts that the value is a file and is writable. - */ - public function toBeWritableFile(): Expectation - { - if (!is_string($this->value)) { - InvalidExpectationValue::expected('string'); - } - Assert::assertFileIsWritable($this->value); - - return $this; - } - - /** - * Asserts that the value array matches the given array subset. - * - * @param iterable $array - */ - public function toMatchArray(iterable|object $array): Expectation - { - if (is_object($this->value) && method_exists($this->value, 'toArray')) { - $valueAsArray = $this->value->toArray(); - } else { - $valueAsArray = (array) $this->value; - } - - foreach ($array as $key => $value) { - Assert::assertArrayHasKey($key, $valueAsArray); - - Assert::assertEquals( - $value, - $valueAsArray[$key], - sprintf( - 'Failed asserting that an array has a key %s with the value %s.', - $this->export($key), - $this->export($valueAsArray[$key]), - ), - ); - } - - return $this; - } - - /** - * Asserts that the value object matches a subset - * of the properties of an given object. - * - * @param iterable|object $object - */ - public function toMatchObject(iterable|object $object): Expectation - { - foreach ((array) $object as $property => $value) { - if (!is_object($this->value) && !is_string($this->value)) { - InvalidExpectationValue::expected('object|string'); - } - - Assert::assertTrue(property_exists($this->value, $property)); - - /* @phpstan-ignore-next-line */ - $propertyValue = $this->value->{$property}; - Assert::assertEquals( - $value, - $propertyValue, - sprintf( - 'Failed asserting that an object has a property %s with the value %s.', - $this->export($property), - $this->export($propertyValue), - ), - ); - } - - return $this; - } - - /** - * Asserts that the value matches a regular expression. - */ - public function toMatch(string $expression): Expectation - { - if (!is_string($this->value)) { - InvalidExpectationValue::expected('string'); - } - Assert::assertMatchesRegularExpression($expression, $this->value); - - return $this; - } - - /** - * Asserts that the value matches a constraint. - */ - public function toMatchConstraint(Constraint $constraint): Expectation - { - Assert::assertThat($this->value, $constraint); - - return $this; - } - - /** - * Asserts that executing value throws an exception. - * - * @param (Closure(Throwable): mixed)|string $exception - */ - public function toThrow(callable|string $exception, string $exceptionMessage = null): Expectation - { - $callback = NullClosure::create(); - - if ($exception instanceof Closure) { - $callback = $exception; - $parameters = (new ReflectionFunction($exception))->getParameters(); - - if (1 !== count($parameters)) { - throw new InvalidArgumentException('The given closure must have a single parameter type-hinted as the class string.'); - } - - if (!($type = $parameters[0]->getType()) instanceof ReflectionNamedType) { - throw new InvalidArgumentException('The given closure\'s parameter must be type-hinted as the class string.'); - } - - $exception = $type->getName(); - } - - try { - ($this->value)(); - } catch (Throwable $e) { // @phpstan-ignore-line - if (!class_exists($exception)) { - Assert::assertStringContainsString($exception, $e->getMessage()); - - return $this; - } - - if ($exceptionMessage !== null) { - Assert::assertStringContainsString($exceptionMessage, $e->getMessage()); - } - - Assert::assertInstanceOf($exception, $e); - $callback($e); - - return $this; - } - - if (!class_exists($exception)) { - throw new ExpectationFailedException("Exception with message \"$exception\" not thrown."); - } - - throw new ExpectationFailedException("Exception \"$exception\" not thrown."); - } - - /** - * Exports the given value. - */ - private function export(mixed $value): string - { - if ($this->exporter === null) { - $this->exporter = new Exporter(); - } - - return $this->exporter->export($value); - } - /** * Dynamically handle calls to the class or * creates a new higher order expectation. * * @param array $parameters - * - * @return HigherOrderExpectation|mixed */ - public function __call(string $method, array $parameters) + public function __call(string $method, array $parameters): Expectation|HigherOrderExpectation { - if (!Expectation::hasExtend($method)) { + if (!$this->hasExpectation($method)) { /* @phpstan-ignore-next-line */ return new HigherOrderExpectation($this, $this->value->$method(...$parameters)); } - return $this->__extendsCall($method, $parameters); + ExpectationPipeline::for($this->getExpectationClosure($method)) + ->send(...$parameters) + ->through($this->pipes($method, $this, Expectation::class)) + ->run(); + + return $this; + } + + private function getExpectationClosure(string $name): Closure + { + if (method_exists($this->coreExpectation, $name)) { + //@phpstan-ignore-next-line + return Closure::fromCallable([$this->coreExpectation, $name]); + } + + if (self::hasExtend($name)) { + $extend = self::$extends[$name]->bindTo($this, Expectation::class); + + if ($extend != false) { + return $extend; + } + } + + throw PipeException::expectationNotFound($name); + } + + private function hasExpectation(string $name): bool + { + if (method_exists($this->coreExpectation, $name)) { + return true; + } + + if (self::hasExtend($name)) { + return true; + } + + return false; } /** * Dynamically calls methods on the class without any arguments * or creates a new higher order expectation. + * + * @return Expectation|OppositeExpectation|Each|HigherOrderExpectation|TValue */ - public function __get(string $name): Expectation|OppositeExpectation|Each|HigherOrderExpectation + public function __get(string $name) { - if (!method_exists($this, $name) && !Expectation::hasExtend($name)) { - //@phpstan-ignore-next-line + if ($name === 'value') { + return $this->coreExpectation->value; + } + + if (!method_exists($this, $name) && !method_exists($this->coreExpectation, $name) && !Expectation::hasExtend($name)) { return new HigherOrderExpectation($this, $this->retrieve($name, $this->value)); } /* @phpstan-ignore-next-line */ return $this->{$name}(); } + + public static function hasMethod(string $name): bool + { + return method_exists(CoreExpectation::class, $name); + } } diff --git a/src/Support/ExpectationPipeline.php b/src/Support/ExpectationPipeline.php index 9ebba221..0ddc9260 100644 --- a/src/Support/ExpectationPipeline.php +++ b/src/Support/ExpectationPipeline.php @@ -6,35 +6,30 @@ namespace Pest\Support; use Closure; +/** + * @internal + */ final class ExpectationPipeline { /** @var array */ - private $pipes = []; + private array $pipes = []; /** @var array */ - private $passable; + private array $passable; - /** @var Closure */ - private $expectationClosure; + private Closure $expectationClosure; - /** @var string */ - private $expectationName; - - public function __construct(string $expectationName, Closure $expectationClosure) + public function __construct(Closure $expectationClosure) { $this->expectationClosure = $expectationClosure; - $this->expectationName = $expectationName; } - public static function for(string $expectationName, Closure $expectationClosure): self + public static function for(Closure $expectationClosure): self { - return new self($expectationName, $expectationClosure); + return new self($expectationClosure); } - /** - * @param array $passable - */ - public function send(...$passable): self + public function send(mixed ...$passable): self { $this->passable = $passable;