Skip to content

Commit ef9ba6f

Browse files
committed
TestSetterAndGetter: Allow setting target for a particular test set.
1 parent 867717d commit ef9ba6f

File tree

2 files changed

+164
-14
lines changed

2 files changed

+164
-14
lines changed

src/TestCase/TestSetterAndGetterTrait.php

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?php
1+
<?php declare(strict_types=1);
22
/**
33
* CROSS PHPUnit Utils
44
*
@@ -7,11 +7,10 @@
77
* @copyright 2019 Cross Solution <http://cross-solution.de>
88
*/
99

10-
declare(strict_types=1);
11-
1210
namespace Cross\TestUtils\TestCase;
1311

1412
use Cross\TestUtils\Exception\InvalidUsageException;
13+
use Cross\TestUtils\Utils\Instance;
1514
use Cross\TestUtils\Utils\Target;
1615

1716
/**
@@ -36,6 +35,15 @@
3635
*
3736
* Available keys in the <spec> array:
3837
*
38+
* * 'target': Allows to specify a SUT for this particular test only.
39+
* The value must be either an object a string representing a FQCN
40+
* or an array [FQCN, arg, ...]
41+
*
42+
* * target_callback': Get the SUT via a callback.
43+
* If a string is given, it is assumed that a method
44+
* in the TestCase is meant.
45+
* The callbakc must return an object.
46+
*
3947
* * 'value': The value to test the setter and getter with.
4048
* First the setter will be called with the value as argument.
4149
* Then the assertion will gets called, passing in the value and
@@ -88,6 +96,8 @@
8896
* @property array $setterAndGetter
8997
*
9098
* @author Mathias Gelhausen <gelhausen@cross-solution.de>
99+
*
100+
* @since @#next#@ Allow SUT per individual test.
91101
*/
92102
trait TestSetterAndGetterTrait
93103
{
@@ -123,16 +133,9 @@ public function testSetterAndGetter($name, $spec = null): void
123133
return;
124134
}
125135

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'];
136+
$target = $this->setterAndGetterGetTarget($spec);
137+
$spec = $this->setterAndGetterNormalizeSpec($spec, $name, $target);
138+
$value = $spec['value'];
136139

137140
if ($spec['exception']) {
138141
if (is_array($spec['exception'])) {
@@ -171,9 +174,52 @@ public function testSetterAndGetter($name, $spec = null): void
171174
}
172175
}
173176

177+
/**
178+
* @param string|array $spec
179+
* @internal
180+
*/
181+
private function setterAndGetterGetTarget($spec): object
182+
{
183+
if (isset($spec['target'])) {
184+
return
185+
is_object($spec['target'])
186+
? $spec['target']
187+
: Instance::withMappedArguments($spec['target'], $this)
188+
;
189+
}
190+
191+
if (isset($spec['target_callback'])) {
192+
$cb = $spec['target_callback'];
193+
if (is_string($cb)) {
194+
$cb = [$this, $cb];
195+
}
196+
197+
if (!is_callable($cb)) {
198+
throw InvalidUsageException::fromTrait(__TRAIT__, __CLASS__, 'Invalid target callback.');
199+
}
200+
201+
$target = $cb();
202+
203+
if (!is_object($target)) {
204+
throw InvalidUsageException::fromTrait(__TRAIT__, __CLASS__, 'Target callback must return an object.');
205+
}
206+
207+
return $target;
208+
}
209+
210+
return Target::get(
211+
$this,
212+
['getSetterAndGetterTarget', 'getTarget'],
213+
['setterAndGetterTarget', 'target'],
214+
'setterAndGetter',
215+
true
216+
);
217+
}
218+
174219
/**
175220
* Normalize the test specification.
176221
*
222+
* @internal
177223
* @param array|string $spec
178224
* @param string $name
179225
* @param object $target

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)