Skip to content

Commit 70665bb

Browse files
Added once architecture for mock functions
1 parent 729ff8b commit 70665bb

File tree

5 files changed

+145
-1
lines changed

5 files changed

+145
-1
lines changed

README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,4 +515,60 @@ $mockFunction = new MockFunction();
515515
$mockFunction->getCalledCountOutScope('time'); //3
516516

517517
$mockFunction->getTotalCount('time'); //5
518+
```
519+
### The restore mock function
520+
You can convert a mock function to php's original function, that is, restore it.
521+
```php
522+
$mockFunction = new MockFunction();
523+
524+
525+
$mockFunction->addIfNotDefined('date', '2010-01-01');
526+
527+
528+
$mockFunction->scope();
529+
530+
echo $date->now(); //2010-01-01,its return the mock function value
531+
$mockFunction->restoreOriginalFunction('date');
532+
echo $date->now(); //2025-02-28, its return the real value of date function
533+
534+
535+
$mockFunction->endScope();
536+
```
537+
or
538+
```php
539+
$mockFunction = new MockFunction();
540+
$mockFunction->addIfNotDefined('date', '2010-01-01');
541+
542+
$mockFunction->runWithMock(new Date(), function (Date $date) use ($mockFunction) {
543+
echo $date->now(); //2010-01-01
544+
$mockFunction->restoreOriginalFunction('date');
545+
echo $date->now(); // it will return current date not the mock function
546+
});
547+
```
548+
549+
### the once for the mock functions
550+
You may want mock functions to run only once.
551+
Here is the simple usage
552+
```php
553+
$mockFunction = new MockFunction();
554+
555+
$mockFunction->once(function (MockFunction $function){
556+
$function->add('date', '2022-01-01');
557+
$function->add('time', 100);
558+
});
559+
560+
$mockFunction->scope();
561+
echo date('y-m-d'); //it will work and will return the 2010-01-01
562+
echo date('y-m-d');//it will throw exception, because it will work just once
563+
564+
echo time(); //it will work and will return the 100
565+
echo time(); ////it will throw exception, because it will work just once
566+
///
567+
$mockFunction->endScope();
568+
569+
echo date('y-m-d'); //2025-02-28, it will work,because it's out of the scope
570+
echo date('y-m-d'); //2025-02-28, it will work,because it's out of the scope
571+
echo date('y-m-d'); //2025-02-28, it will work,because it's out of the scope
572+
echo time(); //it will work and will return the real time
573+
518574
```

src/MockFunction.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ class MockFunction
1515
* @var array<string,Closure>
1616
*/
1717
private array $functions = [];
18+
19+
/**
20+
* @var array<string, Closure> $originalFunctions
21+
*/
22+
private array $originalFunctions = [];
1823
/**
1924
* @var bool
2025
*/
@@ -33,6 +38,15 @@ class MockFunction
3338
*/
3439
private static array $createdFunctions = [];
3540

41+
42+
private array $onceFunctions = [];
43+
44+
45+
/**
46+
* @var bool $onceMode
47+
*/
48+
private bool $onceMode = false;
49+
3650
/**
3751
* @var array<string, array{in_scope: int, out_scope: int}> $calledCount
3852
*/
@@ -46,19 +60,40 @@ public function __construct()
4660
static::$instances = $this;
4761
}
4862

63+
4964
/**
5065
* @param string $name
5166
* @param Closure $returnValue
5267
* @return void
5368
*/
5469
public function add(string $name, mixed $returnValue): void
5570
{
71+
if ($this->onceMode && !in_array($name, $this->onceFunctions, true)) {
72+
$this->onceFunctions[] = $name;
73+
}
74+
if (!isset($this->originalFunctions[$name]) && function_exists($name)) {
75+
$this->originalFunctions[$name] = $name(...);
76+
}
77+
5678
if (!($returnValue instanceof Closure)) {
5779
$returnValue = static fn() => $returnValue;
5880
}
81+
5982
$this->functions[$name] = $returnValue;
6083
}
6184

85+
/***
86+
* @noinspection PhpUnused
87+
* @param string $name
88+
* @return void
89+
*/
90+
public function restoreOriginalFunction(string $name): void
91+
{
92+
if (isset($this->originalFunctions[$name])) {
93+
$this->functions[$name] = $this->originalFunctions[$name];
94+
}
95+
}
96+
6297
/**
6398
* @noinspection PhpUnused
6499
* @param string $name
@@ -88,6 +123,12 @@ public function has(string $name): bool
88123
*/
89124
private function call(string $name, array $args): mixed
90125
{
126+
if (in_array($name, $this->onceFunctions, true)) {
127+
$calledCount = $this->getCalledCountInScope($name);
128+
if ($calledCount !== 0) {
129+
throw new OnceMockFunction("$name cant be called more than once ");
130+
}
131+
}
91132
return $this->executeWithEffect(
92133
call_user_func($this->functions[$name], ...$args),
93134
fn() => $this->incrementCount($name)
@@ -265,6 +306,13 @@ public function runWithMock(object $object, Closure $closure): mixed
265306
return $result;
266307
}
267308

309+
310+
public function once(Closure $closure):void
311+
{
312+
$this->onceMode = true;
313+
$closure($this);
314+
$this->onceMode = false;
315+
}
268316
/**
269317
* @param string $method
270318
* @param array $arguments

src/OnceMockFunction.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Zeus\Mock;
4+
5+
use RuntimeException;
6+
7+
class OnceMockFunction extends RuntimeException
8+
{}

tests/stubs/Date.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ class Date
88

99
public function now():string
1010
{
11-
return date('y-m-d');
11+
return date('Y-m-d');
1212
}
1313
}

tests/unit/OnceMockTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Zeus\Mock\Tests\unit;
4+
5+
use PHPUnit\Framework\Attributes\Test;
6+
use PHPUnit\Framework\TestCase;
7+
use Zeus\Mock\MockFunction;
8+
use Zeus\Mock\OnceMockFunction;
9+
10+
class OnceMockTest extends TestCase
11+
{
12+
13+
14+
#[Test]
15+
public function onceMock(): void
16+
{
17+
18+
$mockFunction = new MockFunction();
19+
$mockFunction->once(function (MockFunction $function) {
20+
$function->add('time', function () {
21+
return 100;
22+
});
23+
});
24+
25+
$mockFunction->scope();
26+
$this->assertEquals(100, time());
27+
$this->expectException(OnceMockFunction::class);
28+
time();
29+
$mockFunction->endScope();
30+
$this->assertNotEquals(100, time());
31+
}
32+
}

0 commit comments

Comments
 (0)