From 867717d4f5df44f51678c8d2f5721e74303c76eb Mon Sep 17 00:00:00 2001 From: Mathias Gelhausen Date: Thu, 26 Sep 2019 12:27:57 +0200 Subject: [PATCH 1/5] TestSetterAndGetter: Use InvalidUsageExceptions. --- src/TestCase/TestSetterAndGetterTrait.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/TestCase/TestSetterAndGetterTrait.php b/src/TestCase/TestSetterAndGetterTrait.php index 2349d48..beea1d4 100644 --- a/src/TestCase/TestSetterAndGetterTrait.php +++ b/src/TestCase/TestSetterAndGetterTrait.php @@ -198,10 +198,8 @@ private function setterAndGetterNormalizeSpec($spec, string $name, object $targe return $normalized; } - $err = __TRAIT__ . ': ' . get_class($this) . ': '; - if (!is_array($spec)) { - throw new \PHPUnit\Framework\Exception($err . 'Invalid specification. Must be array.'); + throw InvalidUsageException::fromTrait(__TRAIT__, __CLASS__, 'Invalid specification. Must be array.'); } foreach ($spec as $key => $value) { @@ -258,7 +256,11 @@ private function setterAndGetterNormalizeSpec($spec, string $name, object $targe } if (!is_callable($value)) { - throw new \PHPUnit\Framework\Exception($err . 'Invalid callback for "' . $key . '".'); + throw InvalidUsageException::fromTrait( + __TRAIT__, + __CLASS__, + 'Invalid callback for "' . $key . '".' + ); } $key = substr($key, 0, -9); @@ -277,7 +279,11 @@ private function setterAndGetterNormalizeSpec($spec, string $name, object $targe } if (!is_callable($value)) { - throw new \PHPUnit\Framework\Exception($err . 'Invalid callback for "' . $key . '".'); + throw InvalidUsageException::fromTrait( + __TRAIT__, + __CLASS__, + 'Invalid callback for "' . $key . '".' + ); } break; From ef9ba6fe8a97e158f94298ed1028e23dc467254a Mon Sep 17 00:00:00 2001 From: Mathias Gelhausen Date: Fri, 27 Sep 2019 16:11:41 +0200 Subject: [PATCH 2/5] TestSetterAndGetter: Allow setting target for a particular test set. --- src/TestCase/TestSetterAndGetterTrait.php | 72 +++++++++--- .../TestCase/TestSetterAndGetterTraitTest.php | 106 +++++++++++++++++- 2 files changed, 164 insertions(+), 14 deletions(-) diff --git a/src/TestCase/TestSetterAndGetterTrait.php b/src/TestCase/TestSetterAndGetterTrait.php index beea1d4..e18e22b 100644 --- a/src/TestCase/TestSetterAndGetterTrait.php +++ b/src/TestCase/TestSetterAndGetterTrait.php @@ -1,4 +1,4 @@ - */ -declare(strict_types=1); - namespace Cross\TestUtils\TestCase; use Cross\TestUtils\Exception\InvalidUsageException; +use Cross\TestUtils\Utils\Instance; use Cross\TestUtils\Utils\Target; /** @@ -36,6 +35,15 @@ * * Available keys in the array: * + * * 'target': Allows to specify a SUT for this particular test only. + * The value must be either an object a string representing a FQCN + * or an array [FQCN, arg, ...] + * + * * target_callback': Get the SUT via a callback. + * If a string is given, it is assumed that a method + * in the TestCase is meant. + * The callbakc must return an object. + * * * 'value': The value to test the setter and getter with. * First the setter will be called with the value as argument. * Then the assertion will gets called, passing in the value and @@ -88,6 +96,8 @@ * @property array $setterAndGetter * * @author Mathias Gelhausen + * + * @since @#next#@ Allow SUT per individual test. */ trait TestSetterAndGetterTrait { @@ -123,16 +133,9 @@ public function testSetterAndGetter($name, $spec = null): void return; } - $target = Target::get( - $this, - ['getSetterAndGetterTarget', 'getTarget'], - ['setterAndGetterTarget', 'target'], - 'setterAndGetter', - true - ); - - $spec = $this->setterAndGetterNormalizeSpec($spec, $name, $target); - $value = $spec['value']; + $target = $this->setterAndGetterGetTarget($spec); + $spec = $this->setterAndGetterNormalizeSpec($spec, $name, $target); + $value = $spec['value']; if ($spec['exception']) { if (is_array($spec['exception'])) { @@ -171,9 +174,52 @@ public function testSetterAndGetter($name, $spec = null): void } } + /** + * @param string|array $spec + * @internal + */ + private function setterAndGetterGetTarget($spec): object + { + if (isset($spec['target'])) { + return + is_object($spec['target']) + ? $spec['target'] + : Instance::withMappedArguments($spec['target'], $this) + ; + } + + if (isset($spec['target_callback'])) { + $cb = $spec['target_callback']; + if (is_string($cb)) { + $cb = [$this, $cb]; + } + + if (!is_callable($cb)) { + throw InvalidUsageException::fromTrait(__TRAIT__, __CLASS__, 'Invalid target callback.'); + } + + $target = $cb(); + + if (!is_object($target)) { + throw InvalidUsageException::fromTrait(__TRAIT__, __CLASS__, 'Target callback must return an object.'); + } + + return $target; + } + + return Target::get( + $this, + ['getSetterAndGetterTarget', 'getTarget'], + ['setterAndGetterTarget', 'target'], + 'setterAndGetter', + true + ); + } + /** * Normalize the test specification. * + * @internal * @param array|string $spec * @param string $name * @param object $target diff --git a/test/TestUtilsTest/TestCase/TestSetterAndGetterTraitTest.php b/test/TestUtilsTest/TestCase/TestSetterAndGetterTraitTest.php index ff3b5dc..b0c0d54 100644 --- a/test/TestUtilsTest/TestCase/TestSetterAndGetterTraitTest.php +++ b/test/TestUtilsTest/TestCase/TestSetterAndGetterTraitTest.php @@ -101,6 +101,110 @@ public function __call($method, $args) } + public function provideIndividualTargetData() + { + $obj = new \stdClass; + $obj2 = new class ('', '') { + public $arg1; + public $arg2; + public function __construct($arg1, $arg2) + { + $this->arg1 = $arg1; + $this->arg2 = $arg2; + } + }; + + return [ + [ + ['target' => \stdClass::class], + \stdClass::class + ], + [ + ['target' => $obj], + $obj + ], + [ + ['target' => [get_class($obj2), 'arg1', 'arg2']], + [get_class($obj2), ['arg1' => 'arg1', 'arg2' => 'arg2']] + ], + + [ + ['target_callback' => function () use ($obj) { return $obj; }], + $obj, + ], + [ + ['target_callback' => 'getSut'], + \stdClass::class, + ], + ]; + } + + /** + * @dataProvider provideIndividualTargetData + */ + public function testAllowsProvidingIndividualTarget(array $spec, $expect): void + { + $target = new class { + use TestSetterAndGetterTrait { + TestSetterAndGetterTrait::setterAndGetterGetTarget as originalGetTarget; + } + + public $sut; + + public function testSetterAndGetter($name, $spec=null) { + $this->testSetterAndGetterGetTarget($spec); + } + + private function testSetterAndGetterGetTarget($spec) { + $this->sut = $this->originalGetTarget($spec); + } + + public function getSut() { + return new \stdClass; + } + }; + + $target->testSetterAndGetter('test', $spec); + + if (is_object($expect)) { + static::assertSame($target->sut, $expect); + } elseif (is_array($expect)) { + static::assertInstanceOf($expect[0], $target->sut); + foreach ($expect[1] as $name => $value) { + static::assertEquals($value, $target->sut->$name); + } + } else { + static::assertInstanceOf($expect, $target->sut); + } + } + + public function testSpecifyInvalidTargetCallbackThrowsException() + { + $target = new class { + use TestSetterAndGetterTrait; + }; + + $this->expectException(InvalidUsageException::class); + $this->expectExceptionMessage('Invalid target callback'); + + $target->testSetterAndGetter('test', ['target_callback' => 'invalidCallback']); + } + + public function testAssureTargetCallbackReturnsObject() + { + $target = new class { + use TestSetterAndGetterTrait; + + public function sut() { + return 'not an object'; + } + }; + + $this->expectException(InvalidUsageException::class); + $this->expectExceptionMessage('must return an object'); + + $target->testSetterAndGetter('test', ['target_callback' => 'sut']); + } public function normalizationData() : array { @@ -213,7 +317,7 @@ public function callback() {} }; if (is_string($expect)) { - $this->expectException(\PHPUnit\Framework\Exception::class); + $this->expectException(InvalidUsageException::class); $this->expectExceptionMessage($expect); } From caa9a0d3c46a2bf8cf785b1ba3a798859431de10 Mon Sep 17 00:00:00 2001 From: Mathias Gelhausen Date: Fri, 6 Dec 2019 17:42:58 +0100 Subject: [PATCH 3/5] TestSetterAndGetter: Do not assume TestCase method, if string callback is callable --- src/TestCase/TestSetterAndGetterTrait.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/TestCase/TestSetterAndGetterTrait.php b/src/TestCase/TestSetterAndGetterTrait.php index e18e22b..2736988 100644 --- a/src/TestCase/TestSetterAndGetterTrait.php +++ b/src/TestCase/TestSetterAndGetterTrait.php @@ -190,7 +190,8 @@ private function setterAndGetterGetTarget($spec): object if (isset($spec['target_callback'])) { $cb = $spec['target_callback']; - if (is_string($cb)) { + + if (is_string($cb) && !is_callable($cb)) { $cb = [$this, $cb]; } From c85d1adaeede649f85d937a748a457aa8931dc69 Mon Sep 17 00:00:00 2001 From: Mathias Gelhausen Date: Wed, 19 Feb 2020 11:06:01 +0100 Subject: [PATCH 4/5] TestSetterAndGetter: Apply the stirng callback fix also to 'value_callback', 'setter_callback' and 'expect_callback' --- src/TestCase/TestSetterAndGetterTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TestCase/TestSetterAndGetterTrait.php b/src/TestCase/TestSetterAndGetterTrait.php index 2736988..9d3711d 100644 --- a/src/TestCase/TestSetterAndGetterTrait.php +++ b/src/TestCase/TestSetterAndGetterTrait.php @@ -298,7 +298,7 @@ private function setterAndGetterNormalizeSpec($spec, string $name, object $targe case 'value_callback': case 'setter_value_callback': case 'expect_callback': - if (is_string($value)) { + if (is_string($value) && !is_callable($value)) { $value = [$this, $value]; } From 08fed00c0f4ff2b003f95220e794cd0246e6d754 Mon Sep 17 00:00:00 2001 From: Mathias Gelhausen Date: Thu, 12 Mar 2020 16:39:09 +0100 Subject: [PATCH 5/5] chore(TestSetterAndGetter): Adhere to coding standard introduced with PR #3 --- src/TestCase/TestSetterAndGetterTrait.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/TestCase/TestSetterAndGetterTrait.php b/src/TestCase/TestSetterAndGetterTrait.php index 1de16fa..fb653c4 100644 --- a/src/TestCase/TestSetterAndGetterTrait.php +++ b/src/TestCase/TestSetterAndGetterTrait.php @@ -1,4 +1,4 @@ - */ +declare(strict_types=1); + namespace Cross\TestUtils\TestCase; use Cross\TestUtils\Exception\InvalidUsageException;