Skip to content

Commit 04d34a9

Browse files
committed
Merge pull request #4: (TestSetterAndGetter) Individual target for each test
* pr-4: chore(TestSetterAndGetter): Adhere to coding standard introduced with PR #3 TestSetterAndGetter: Apply the stirng callback fix also to 'value_callback', 'setter_callback' and 'expect_callback' TestSetterAndGetter: Do not assume TestCase method, if string callback is callable TestSetterAndGetter: Allow setting target for a particular test set. TestSetterAndGetter: Use InvalidUsageExceptions.
2 parents 518f02b + 08fed00 commit 04d34a9

File tree

2 files changed

+176
-17
lines changed

2 files changed

+176
-17
lines changed

src/TestCase/TestSetterAndGetterTrait.php

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Cross\TestUtils\TestCase;
1313

1414
use Cross\TestUtils\Exception\InvalidUsageException;
15+
use Cross\TestUtils\Utils\Instance;
1516
use Cross\TestUtils\Utils\Target;
1617

1718
/**
@@ -36,6 +37,15 @@
3637
*
3738
* Available keys in the <spec> array:
3839
*
40+
* * 'target': Allows to specify a SUT for this particular test only.
41+
* The value must be either an object a string representing a FQCN
42+
* or an array [FQCN, arg, ...]
43+
*
44+
* * target_callback': Get the SUT via a callback.
45+
* If a string is given, it is assumed that a method
46+
* in the TestCase is meant.
47+
* The callbakc must return an object.
48+
*
3949
* * 'value': The value to test the setter and getter with.
4050
* First the setter will be called with the value as argument.
4151
* Then the assertion will gets called, passing in the value and
@@ -88,6 +98,8 @@
8898
* @property array $setterAndGetter
8999
*
90100
* @author Mathias Gelhausen <[email protected]>
101+
*
102+
* @since @#next#@ Allow SUT per individual test.
91103
*/
92104
trait TestSetterAndGetterTrait
93105
{
@@ -123,16 +135,9 @@ public function testSetterAndGetter($name, $spec = null): void
123135
return;
124136
}
125137

126-
$target = Target::get(
127-
$this,
128-
['getSetterAndGetterTarget', 'getTarget'],
129-
['setterAndGetterTarget', 'target'],
130-
'setterAndGetter',
131-
true
132-
);
133-
134-
$spec = $this->setterAndGetterNormalizeSpec($spec, $name, $target);
135-
$value = $spec['value'];
138+
$target = $this->setterAndGetterGetTarget($spec);
139+
$spec = $this->setterAndGetterNormalizeSpec($spec, $name, $target);
140+
$value = $spec['value'];
136141

137142
if ($spec['exception']) {
138143
if (is_array($spec['exception'])) {
@@ -171,9 +176,53 @@ public function testSetterAndGetter($name, $spec = null): void
171176
}
172177
}
173178

179+
/**
180+
* @param string|array $spec
181+
* @internal
182+
*/
183+
private function setterAndGetterGetTarget($spec): object
184+
{
185+
if (isset($spec['target'])) {
186+
return
187+
is_object($spec['target'])
188+
? $spec['target']
189+
: Instance::withMappedArguments($spec['target'], $this)
190+
;
191+
}
192+
193+
if (isset($spec['target_callback'])) {
194+
$cb = $spec['target_callback'];
195+
196+
if (is_string($cb) && !is_callable($cb)) {
197+
$cb = [$this, $cb];
198+
}
199+
200+
if (!is_callable($cb)) {
201+
throw InvalidUsageException::fromTrait(__TRAIT__, __CLASS__, 'Invalid target callback.');
202+
}
203+
204+
$target = $cb();
205+
206+
if (!is_object($target)) {
207+
throw InvalidUsageException::fromTrait(__TRAIT__, __CLASS__, 'Target callback must return an object.');
208+
}
209+
210+
return $target;
211+
}
212+
213+
return Target::get(
214+
$this,
215+
['getSetterAndGetterTarget', 'getTarget'],
216+
['setterAndGetterTarget', 'target'],
217+
'setterAndGetter',
218+
true
219+
);
220+
}
221+
174222
/**
175223
* Normalize the test specification.
176224
*
225+
* @internal
177226
* @param array|string $spec
178227
* @param string $name
179228
* @param object $target
@@ -198,10 +247,8 @@ private function setterAndGetterNormalizeSpec($spec, string $name, object $targe
198247
return $normalized;
199248
}
200249

201-
$err = __TRAIT__ . ': ' . get_class($this) . ': ';
202-
203250
if (!is_array($spec)) {
204-
throw new \PHPUnit\Framework\Exception($err . 'Invalid specification. Must be array.');
251+
throw InvalidUsageException::fromTrait(__TRAIT__, __CLASS__, 'Invalid specification. Must be array.');
205252
}
206253

207254
foreach ($spec as $key => $value) {
@@ -253,12 +300,16 @@ private function setterAndGetterNormalizeSpec($spec, string $name, object $targe
253300
case 'value_callback':
254301
case 'setter_value_callback':
255302
case 'expect_callback':
256-
if (is_string($value)) {
303+
if (is_string($value) && !is_callable($value)) {
257304
$value = [$this, $value];
258305
}
259306

260307
if (!is_callable($value)) {
261-
throw new \PHPUnit\Framework\Exception($err . 'Invalid callback for "' . $key . '".');
308+
throw InvalidUsageException::fromTrait(
309+
__TRAIT__,
310+
__CLASS__,
311+
'Invalid callback for "' . $key . '".'
312+
);
262313
}
263314

264315
$key = substr($key, 0, -9);
@@ -277,7 +328,11 @@ private function setterAndGetterNormalizeSpec($spec, string $name, object $targe
277328
}
278329

279330
if (!is_callable($value)) {
280-
throw new \PHPUnit\Framework\Exception($err . 'Invalid callback for "' . $key . '".');
331+
throw InvalidUsageException::fromTrait(
332+
__TRAIT__,
333+
__CLASS__,
334+
'Invalid callback for "' . $key . '".'
335+
);
281336
}
282337

283338
break;

test/TestUtilsTest/TestCase/TestSetterAndGetterTraitTest.php

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,110 @@ public function __call($method, $args)
101101

102102
}
103103

104+
public function provideIndividualTargetData()
105+
{
106+
$obj = new \stdClass;
107+
$obj2 = new class ('', '') {
108+
public $arg1;
109+
public $arg2;
110+
public function __construct($arg1, $arg2)
111+
{
112+
$this->arg1 = $arg1;
113+
$this->arg2 = $arg2;
114+
}
115+
};
116+
117+
return [
118+
[
119+
['target' => \stdClass::class],
120+
\stdClass::class
121+
],
122+
[
123+
['target' => $obj],
124+
$obj
125+
],
126+
[
127+
['target' => [get_class($obj2), 'arg1', 'arg2']],
128+
[get_class($obj2), ['arg1' => 'arg1', 'arg2' => 'arg2']]
129+
],
130+
131+
[
132+
['target_callback' => function () use ($obj) { return $obj; }],
133+
$obj,
134+
],
135+
[
136+
['target_callback' => 'getSut'],
137+
\stdClass::class,
138+
],
139+
];
140+
}
141+
142+
/**
143+
* @dataProvider provideIndividualTargetData
144+
*/
145+
public function testAllowsProvidingIndividualTarget(array $spec, $expect): void
146+
{
147+
$target = new class {
148+
use TestSetterAndGetterTrait {
149+
TestSetterAndGetterTrait::setterAndGetterGetTarget as originalGetTarget;
150+
}
151+
152+
public $sut;
153+
154+
public function testSetterAndGetter($name, $spec=null) {
155+
$this->testSetterAndGetterGetTarget($spec);
156+
}
157+
158+
private function testSetterAndGetterGetTarget($spec) {
159+
$this->sut = $this->originalGetTarget($spec);
160+
}
161+
162+
public function getSut() {
163+
return new \stdClass;
164+
}
165+
};
166+
167+
$target->testSetterAndGetter('test', $spec);
168+
169+
if (is_object($expect)) {
170+
static::assertSame($target->sut, $expect);
171+
} elseif (is_array($expect)) {
172+
static::assertInstanceOf($expect[0], $target->sut);
173+
foreach ($expect[1] as $name => $value) {
174+
static::assertEquals($value, $target->sut->$name);
175+
}
176+
} else {
177+
static::assertInstanceOf($expect, $target->sut);
178+
}
179+
}
180+
181+
public function testSpecifyInvalidTargetCallbackThrowsException()
182+
{
183+
$target = new class {
184+
use TestSetterAndGetterTrait;
185+
};
186+
187+
$this->expectException(InvalidUsageException::class);
188+
$this->expectExceptionMessage('Invalid target callback');
189+
190+
$target->testSetterAndGetter('test', ['target_callback' => 'invalidCallback']);
191+
}
192+
193+
public function testAssureTargetCallbackReturnsObject()
194+
{
195+
$target = new class {
196+
use TestSetterAndGetterTrait;
197+
198+
public function sut() {
199+
return 'not an object';
200+
}
201+
};
202+
203+
$this->expectException(InvalidUsageException::class);
204+
$this->expectExceptionMessage('must return an object');
205+
206+
$target->testSetterAndGetter('test', ['target_callback' => 'sut']);
207+
}
104208

105209
public function normalizationData() : array
106210
{
@@ -213,7 +317,7 @@ public function callback() {}
213317
};
214318

215319
if (is_string($expect)) {
216-
$this->expectException(\PHPUnit\Framework\Exception::class);
320+
$this->expectException(InvalidUsageException::class);
217321
$this->expectExceptionMessage($expect);
218322
}
219323

0 commit comments

Comments
 (0)