From 2a8de0565f16a8a72659b183fb8ae89c3b235851 Mon Sep 17 00:00:00 2001 From: luke Date: Fri, 9 Jul 2021 16:50:15 +0100 Subject: [PATCH] Adds support for Higher Order Expectations in Higher Order Tests --- src/PendingObjects/TestCall.php | 22 ++++++++++++++++- src/Support/HigherOrderMessage.php | 24 ++++++++++--------- src/Support/HigherOrderMessageCollection.php | 12 +++++----- tests/Features/Expect/HigherOrder/methods.php | 7 ++++++ .../HigherOrder/methodsAndProperties.php | 15 ++++++++++++ .../Expect/HigherOrder/properties.php | 5 ++++ 6 files changed, 67 insertions(+), 18 deletions(-) diff --git a/src/PendingObjects/TestCall.php b/src/PendingObjects/TestCall.php index 81349be1..81bf9243 100644 --- a/src/PendingObjects/TestCall.php +++ b/src/PendingObjects/TestCall.php @@ -155,12 +155,30 @@ final class TestCall return $this; } + /** + * Saves the property accessors to be used on the target. + */ + public function __get(string $name): self + { + return $this->addChain($name); + } + /** * Saves the calls to be used on the target. * * @param array $arguments */ public function __call(string $name, array $arguments): self + { + return $this->addChain($name, $arguments); + } + + /** + * Add a chain to the test case factory. Omitting the arguments will treat it as a property accessor. + * + * @param array|null $arguments + */ + private function addChain(string $name, array $arguments = null): self { $this->testCaseFactory ->chains @@ -171,7 +189,9 @@ final class TestCall if ($this->testCaseFactory->description !== null) { $this->testCaseFactory->description .= ' → '; } - $this->testCaseFactory->description .= sprintf('%s %s', $name, $exporter->shortenedRecursiveExport($arguments)); + $this->testCaseFactory->description .= $arguments === null + ? $name + : sprintf('%s %s', $name, $exporter->shortenedRecursiveExport($arguments)); } return $this; diff --git a/src/Support/HigherOrderMessage.php b/src/Support/HigherOrderMessage.php index 78820dc3..67993bc5 100644 --- a/src/Support/HigherOrderMessage.php +++ b/src/Support/HigherOrderMessage.php @@ -34,18 +34,18 @@ final class HigherOrderMessage public $line; /** - * The method name. + * The method or property name to access. * * @readonly * * @var string */ - public $methodName; + public $name; /** * The arguments. * - * @var array + * @var array|null * * @readonly */ @@ -61,13 +61,13 @@ final class HigherOrderMessage /** * Creates a new higher order message. * - * @param array $arguments + * @param array|null $arguments */ - public function __construct(string $filename, int $line, string $methodName, array $arguments) + public function __construct(string $filename, int $line, string $methodName, $arguments) { $this->filename = $filename; $this->line = $line; - $this->methodName = $methodName; + $this->name = $methodName; $this->arguments = $arguments; } @@ -85,21 +85,23 @@ final class HigherOrderMessage if ($this->hasHigherOrderCallable()) { /* @phpstan-ignore-next-line */ - return (new HigherOrderCallables($target))->{$this->methodName}(...$this->arguments); + return (new HigherOrderCallables($target))->{$this->name}(...$this->arguments); } try { - return Reflection::call($target, $this->methodName, $this->arguments); + return is_array($this->arguments) + ? Reflection::call($target, $this->name, $this->arguments) + : $target->{$this->name}; /* @phpstan-ignore-line */ } catch (Throwable $throwable) { Reflection::setPropertyValue($throwable, 'file', $this->filename); Reflection::setPropertyValue($throwable, 'line', $this->line); - if ($throwable->getMessage() === self::getUndefinedMethodMessage($target, $this->methodName)) { + if ($throwable->getMessage() === self::getUndefinedMethodMessage($target, $this->name)) { /** @var ReflectionClass $reflection */ $reflection = new ReflectionClass($target); /* @phpstan-ignore-next-line */ $reflection = $reflection->getParentClass() ?: $reflection; - Reflection::setPropertyValue($throwable, 'message', sprintf('Call to undefined method %s::%s()', $reflection->getName(), $this->methodName)); + Reflection::setPropertyValue($throwable, 'message', sprintf('Call to undefined method %s::%s()', $reflection->getName(), $this->name)); } throw $throwable; @@ -125,7 +127,7 @@ final class HigherOrderMessage */ private function hasHigherOrderCallable() { - return in_array($this->methodName, get_class_methods(HigherOrderCallables::class), true); + return in_array($this->name, get_class_methods(HigherOrderCallables::class), true); } private static function getUndefinedMethodMessage(object $target, string $methodName): string diff --git a/src/Support/HigherOrderMessageCollection.php b/src/Support/HigherOrderMessageCollection.php index 4c6e9cd2..b107bdba 100644 --- a/src/Support/HigherOrderMessageCollection.php +++ b/src/Support/HigherOrderMessageCollection.php @@ -17,21 +17,21 @@ final class HigherOrderMessageCollection /** * Adds a new higher order message to the collection. * - * @param array $arguments + * @param array|null $arguments */ - public function add(string $filename, int $line, string $methodName, array $arguments): void + public function add(string $filename, int $line, string $name, array $arguments = null): void { - $this->messages[] = new HigherOrderMessage($filename, $line, $methodName, $arguments); + $this->messages[] = new HigherOrderMessage($filename, $line, $name, $arguments); } /** * Adds a new higher order message to the collection if the callable condition is does not return false. * - * @param array $arguments + * @param array|null $arguments */ - public function addWhen(callable $condition, string $filename, int $line, string $methodName, array $arguments): void + public function addWhen(callable $condition, string $filename, int $line, string $name, array $arguments = null): void { - $this->messages[] = (new HigherOrderMessage($filename, $line, $methodName, $arguments))->when($condition); + $this->messages[] = (new HigherOrderMessage($filename, $line, $name, $arguments))->when($condition); } /** diff --git a/tests/Features/Expect/HigherOrder/methods.php b/tests/Features/Expect/HigherOrder/methods.php index 66329b73..d6da636b 100644 --- a/tests/Features/Expect/HigherOrder/methods.php +++ b/tests/Features/Expect/HigherOrder/methods.php @@ -67,6 +67,13 @@ it('can handle nested method calls', function () { ->books()->each->toBeArray(); }); +it('works with higher order tests') + ->expect(new HasMethods()) + ->newInstance()->newInstance()->name()->toEqual('Has Methods')->toBeString() + ->newInstance()->name()->toEqual('Has Methods')->not->toBeArray + ->name()->toEqual('Has Methods') + ->books()->each->toBeArray; + class HasMethods { public function name() diff --git a/tests/Features/Expect/HigherOrder/methodsAndProperties.php b/tests/Features/Expect/HigherOrder/methodsAndProperties.php index 08c4a3bc..b98c9f16 100644 --- a/tests/Features/Expect/HigherOrder/methodsAndProperties.php +++ b/tests/Features/Expect/HigherOrder/methodsAndProperties.php @@ -22,6 +22,13 @@ it('can handle nested methods and properties', function () { ->newInstance()->books()->toBeArray(); }); +it('works with higher order tests') + ->expect(new HasMethodsAndProperties()) + ->meta->foo->bar->toBeString()->toEqual('baz')->not->toBeInt + ->newInstance()->meta->foo->toBeArray + ->newInstance()->multiply(2, 2)->toEqual(4)->not->toEqual(5) + ->newInstance()->books()->toBeArray(); + it('can start a new higher order expectation using the and syntax', function () { expect(new HasMethodsAndProperties()) ->toBeInstanceOf(HasMethodsAndProperties::class) @@ -33,6 +40,14 @@ it('can start a new higher order expectation using the and syntax', function () expect(static::getCount())->toEqual(4); }); +it('can start a new higher order expectation using the and syntax in higher order tests') + ->expect(new HasMethodsAndProperties()) + ->toBeInstanceOf(HasMethodsAndProperties::class) + ->meta->toBeArray + ->and(['foo' => 'bar']) + ->toBeArray() + ->foo->toEqual('bar'); + class HasMethodsAndProperties { public $name = 'Has Methods and Properties'; diff --git a/tests/Features/Expect/HigherOrder/properties.php b/tests/Features/Expect/HigherOrder/properties.php index 154d17e4..5595b9ab 100644 --- a/tests/Features/Expect/HigherOrder/properties.php +++ b/tests/Features/Expect/HigherOrder/properties.php @@ -64,6 +64,11 @@ it('works with nested properties', function () { ->posts->toBeArray()->toHaveCount(2); }); +it('works with higher order tests') + ->expect(new HasProperties()) + ->nested->foo->bar->toBeString()->toEqual('baz') + ->posts->toBeArray()->toHaveCount(2); + class HasProperties { public $name = 'foo';