Skip to content

Commit ff10fbd

Browse files
committed
Add ability for Expected methods to take in Stubs
or ConsecutiveMap
1 parent 58751ae commit ff10fbd

File tree

5 files changed

+118
-10
lines changed

5 files changed

+118
-10
lines changed

src/Stub.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
use PHPUnit\Framework\MockObject\Generator;
1313
use PHPUnit\Framework\MockObject\MockObject as PHPUnitMockObject;
1414
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount;
15-
use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls;
1615
use PHPUnit\Framework\MockObject\Stub\ReturnCallback;
1716
use PHPUnit\Framework\MockObject\Stub\ReturnStub;
17+
use PHPUnit\Framework\MockObject\Stub\Stub as MockObjectStub;
1818
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
1919
use PHPUnit\Runner\Version as PHPUnitVersion;
2020
use ReflectionClass;
@@ -497,7 +497,7 @@ protected static function bindParameters($mock, array $params)
497497
$mock
498498
->expects($marshaler->getMatcher())
499499
->method($param)
500-
->will(new ReturnCallback($marshaler->getValue()));
500+
->will($marshaler->getValue());
501501
} elseif ($value instanceof Closure) {
502502
$mock
503503
->expects(new AnyInvokedCount)
@@ -508,7 +508,13 @@ protected static function bindParameters($mock, array $params)
508508
$mock
509509
->expects(new AnyInvokedCount)
510510
->method($param)
511-
->will(new ConsecutiveCalls($consecutiveMap->getMap()));
511+
->will($consecutiveMap);
512+
} elseif ($value instanceof MockObjectStub) {
513+
$stub = $value;
514+
$mock
515+
->expects(new AnyInvokedCount)
516+
->method($param)
517+
->will($stub);
512518
} else {
513519
$mock
514520
->expects(new AnyInvokedCount)
@@ -582,6 +588,10 @@ protected static function getMethodsToReplace(ReflectionClass $reflection, array
582588
* $user->getName(); //sam
583589
* $user->getName(); //amy
584590
* ```
591+
*
592+
* It also takes in PHPUnit Stubs.
593+
*
594+
* $user = Stub::make('User', ['getName' => Stub::consecutive(new ReturnCallback([fn() => 'david', fn() => 'emma', fn() => 'sam', fn() => 'amy']))]);
585595
*/
586596
public static function consecutive(): ConsecutiveMap
587597
{

src/Stub/ConsecutiveMap.php

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,45 @@
44

55
namespace Codeception\Stub;
66

7+
use PHPUnit\Framework\MockObject\Invocation;
8+
use PHPUnit\Framework\MockObject\Stub\Stub;
9+
use SebastianBergmann\Exporter\Exporter;
10+
711
/**
812
* Holds matcher and value of mocked method
913
*/
10-
class ConsecutiveMap
14+
class ConsecutiveMap implements Stub
1115
{
1216
private array $consecutiveMap = [];
1317

18+
/**
19+
* @var mixed
20+
*/
21+
private $value;
22+
1423
public function __construct(array $consecutiveMap)
1524
{
1625
$this->consecutiveMap = $consecutiveMap;
1726
}
1827

19-
public function getMap(): array
28+
public function invoke(Invocation $invocation)
29+
{
30+
$this->value = array_shift($this->consecutiveMap);
31+
32+
if ($this->value instanceof Stub) {
33+
$this->value = $this->value->invoke($invocation);
34+
}
35+
36+
return $this->value;
37+
}
38+
39+
public function toString(): string
2040
{
21-
return $this->consecutiveMap;
41+
$exporter = new Exporter;
42+
43+
return sprintf(
44+
'return user-specified value %s',
45+
$exporter->export($this->value),
46+
);
2247
}
2348
}

src/Stub/Expected.php

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use Closure;
88
use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce;
99
use PHPUnit\Framework\MockObject\Rule\InvokedCount;
10+
use PHPUnit\Framework\MockObject\Stub\ReturnCallback;
11+
use PHPUnit\Framework\MockObject\Stub\Stub;
1012

1113
class Expected
1214
{
@@ -33,7 +35,7 @@ public static function never($params = null): StubMarshaler
3335
{
3436
return new StubMarshaler(
3537
new InvokedCount(0),
36-
self::closureIfNull($params)
38+
self::stubIfClosure($params)
3739
);
3840
}
3941

@@ -65,13 +67,20 @@ public static function never($params = null): StubMarshaler
6567
* Expected::once(function() { return Faker::name(); });
6668
* ```
6769
*
70+
* PHPUnit Stub can also be passed as parameter:
71+
*
72+
* ```php
73+
* <?php
74+
* Expected::exactly(3, new ReturnSelf());
75+
* ```
76+
*
6877
* @param mixed $params
6978
*/
7079
public static function once($params = null): StubMarshaler
7180
{
7281
return new StubMarshaler(
7382
new InvokedCount(1),
74-
self::closureIfNull($params)
83+
self::stubIfClosure($params)
7584
);
7685
}
7786

@@ -103,14 +112,28 @@ public static function once($params = null): StubMarshaler
103112
* <?php
104113
* Expected::atLeastOnce(function() { return Faker::name(); });
105114
* ```
115+
*
116+
* ConsecutiveMap can also be passed as parameter:
117+
*
118+
* ```php
119+
* <?php
120+
* Expected::exactly(3, Stub::consecutive(1,2,3));
121+
* ```
122+
*
123+
* PHPUnit Stub can also be passed as parameter:
124+
*
125+
* ```php
126+
* <?php
127+
* Expected::exactly(3, new ReturnSelf());
128+
* ```
106129
*
107130
* @param mixed $params
108131
*/
109132
public static function atLeastOnce($params = null): StubMarshaler
110133
{
111134
return new StubMarshaler(
112135
new InvokedAtLeastOnce(),
113-
self::closureIfNull($params)
136+
self::stubIfClosure($params)
114137
);
115138
}
116139

@@ -145,17 +168,40 @@ public static function atLeastOnce($params = null): StubMarshaler
145168
* <?php
146169
* Expected::exactly(function() { return Faker::name() });
147170
* ```
171+
*
172+
* ConsecutiveMap can also be passed as parameter:
173+
*
174+
* ```php
175+
* <?php
176+
* Expected::exactly(3, Stub::consecutive(1,2,3));
177+
* ```
178+
*
179+
* PHPUnit Stub can also be passed as parameter:
180+
*
181+
* ```php
182+
* <?php
183+
* Expected::exactly(3, new ReturnSelf());
184+
* ```
148185
*
149186
* @param mixed $params
150187
*/
151188
public static function exactly(int $count, $params = null): StubMarshaler
152189
{
153190
return new StubMarshaler(
154191
new InvokedCount($count),
155-
self::closureIfNull($params)
192+
self::stubIfClosure($params)
156193
);
157194
}
158195

196+
private static function stubIfClosure($params) : Stub
197+
{
198+
if ($params instanceof Stub) {
199+
return $params;
200+
}
201+
202+
return new ReturnCallback(self::closureIfNull($params));
203+
}
204+
159205
private static function closureIfNull($params): Closure
160206
{
161207
if ($params instanceof Closure) {

src/Stub/StubMarshaler.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Codeception\Stub;
66

77
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
8+
use PHPUnit\Framework\MockObject\Stub\Stub;
89

910
/**
1011
* Holds matcher and value of mocked method
@@ -26,6 +27,9 @@ public function getMatcher(): InvocationOrder
2627
return $this->methodMatcher;
2728
}
2829

30+
/**
31+
* @return Stub
32+
*/
2933
public function getValue()
3034
{
3135
return $this->methodValue;

tests/StubTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use Codeception\Stub;
88
use Codeception\Stub\StubMarshaler;
99
use PHPUnit\Framework\MockObject\MockObject;
10+
use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls;
11+
use PHPUnit\Framework\MockObject\Stub\ReturnCallback;
1012
use PHPUnit\Framework\TestCase;
1113

1214
final class StubTest extends TestCase
@@ -274,6 +276,9 @@ public static function matcherProvider(): array
274276
[1, Stub\Expected::exactly(1, fn() => null), null],
275277
[1, Stub\Expected::exactly(1, fn(): string => 'hello world!'), 'hello world!'],
276278
[1, Stub\Expected::exactly(1, 'hello world!'), 'hello world!'],
279+
[1, Stub\Expected::exactly(1, Stub::consecutive('hello world!')), 'hello world!'],
280+
[1, Stub\Expected::exactly(1, Stub::consecutive(new ReturnCallback(fn() => 'hello world!'))), 'hello world!'],
281+
[1, Stub\Expected::exactly(1, new ReturnCallback(fn() => 'hello world!')), 'hello world!'],
277282
];
278283
}
279284

@@ -366,6 +371,24 @@ public function testConsecutive()
366371
$this->assertNull($dummy->helloWorld());
367372
}
368373

374+
public function testConsecutiveWithAnonymousMethods()
375+
{
376+
$dummy = Stub::make('DummyClass', ['helloWorld' => new ConsecutiveCalls([
377+
new ReturnCallback(fn() => 'david'),
378+
new ReturnCallback(fn() => 'emma'),
379+
new ReturnCallback(fn() => 'sam'),
380+
new ReturnCallback(fn() => 'amy')
381+
])]);
382+
383+
$this->assertEquals('david', $dummy->helloWorld());
384+
$this->assertEquals('emma', $dummy->helloWorld());
385+
$this->assertEquals('sam', $dummy->helloWorld());
386+
$this->assertEquals('amy', $dummy->helloWorld());
387+
388+
// Expected null value when no more values
389+
$this->assertNull($dummy->helloWorld());
390+
}
391+
369392
public function testStubPrivateProperties()
370393
{
371394
$tester = Stub::construct(

0 commit comments

Comments
 (0)