Skip to content

Commit 2f1b481

Browse files
faisuctaylorotwell
andauthored
[12.x] Fix Cache spy not working with memoized cache (#57996)
* Fix Cache spy not working with memoized cache * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent d03f650 commit 2f1b481

File tree

3 files changed

+132
-3
lines changed

3 files changed

+132
-3
lines changed

src/Illuminate/Cache/CacheManager.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
1010
use Illuminate\Support\Arr;
1111
use InvalidArgumentException;
12+
use Mockery;
13+
use Mockery\LegacyMockInterface;
1214

1315
/**
1416
* @mixin \Illuminate\Cache\Repository
@@ -81,9 +83,17 @@ public function memo($driver = null)
8183
{
8284
$driver = $driver ?: $this->getDefaultDriver();
8385

84-
$this->app->scopedIf($bindingKey = "cache.__memoized:{$driver}", fn () => $this->repository(
85-
new MemoizedStore($driver, $this->store($driver)), ['events' => false]
86-
));
86+
$bindingKey = "cache.__memoized:{$driver}";
87+
88+
$isSpy = isset($this->app['cache']) && $this->app['cache'] instanceof LegacyMockInterface;
89+
90+
$this->app->scopedIf($bindingKey, function () use ($driver, $isSpy) {
91+
$repository = $this->repository(
92+
new MemoizedStore($driver, $this->store($driver)), ['events' => false]
93+
);
94+
95+
return $isSpy ? Mockery::spy($repository) : $repository;
96+
});
8797

8898
return $this->app->make($bindingKey);
8999
}

src/Illuminate/Support/Facades/Cache.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Illuminate\Support\Facades;
44

5+
use Mockery;
6+
57
/**
68
* @method static \Illuminate\Contracts\Cache\Repository store(string|null $name = null)
79
* @method static \Illuminate\Contracts\Cache\Repository driver(string|null $driver = null)
@@ -71,4 +73,27 @@ protected static function getFacadeAccessor()
7173
{
7274
return 'cache';
7375
}
76+
77+
/**
78+
* Convert the facade into a Mockery spy.
79+
*
80+
* @return \Mockery\MockInterface
81+
*/
82+
public static function spy()
83+
{
84+
if (! static::isMock()) {
85+
$class = static::getMockableClass();
86+
$instance = static::getFacadeRoot();
87+
88+
if ($class && $instance) {
89+
return tap(Mockery::spy($instance)->makePartial(), function ($spy) {
90+
static::swap($spy);
91+
});
92+
}
93+
94+
return tap($class ? Mockery::spy($class) : Mockery::spy(), function ($spy) {
95+
static::swap($spy);
96+
});
97+
}
98+
}
7499
}

tests/Cache/CacheSpyMemoTest.php

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Cache;
4+
5+
use Closure;
6+
use Illuminate\Cache\CacheManager;
7+
use Illuminate\Config\Repository as ConfigRepository;
8+
use Illuminate\Container\Container;
9+
use Illuminate\Support\Facades\Cache;
10+
use Illuminate\Support\Facades\Facade;
11+
use Mockery;
12+
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
13+
use Mockery\LegacyMockInterface;
14+
use PHPUnit\Framework\TestCase;
15+
16+
class CacheSpyMemoTest extends TestCase
17+
{
18+
use MockeryPHPUnitIntegration;
19+
20+
protected function setUp(): void
21+
{
22+
parent::setUp();
23+
24+
$container = new Container;
25+
26+
$container->instance('config', new ConfigRepository([
27+
'cache' => [
28+
'default' => 'array',
29+
'stores' => [
30+
'array' => [
31+
'driver' => 'array',
32+
],
33+
],
34+
],
35+
]));
36+
37+
$container->instance('cache', new CacheManager($container));
38+
39+
Facade::setFacadeApplication($container);
40+
}
41+
42+
protected function tearDown(): void
43+
{
44+
Facade::clearResolvedInstances();
45+
Facade::setFacadeApplication(null);
46+
47+
parent::tearDown();
48+
}
49+
50+
public function test_cache_spy_works_with_memoized_cache()
51+
{
52+
$cache = Cache::spy();
53+
54+
Cache::memo()->remember('key', 60, fn() => 'bar');
55+
56+
$cache->shouldHaveReceived('memo')->once();
57+
}
58+
59+
public function test_cache_spy_tracks_remember_on_memoized_cache_as_described_in_issue()
60+
{
61+
$cache = Cache::spy();
62+
63+
$memoizedCache = Cache::memo();
64+
$value = $memoizedCache->remember('key', 60, fn() => 'bar');
65+
66+
$this->assertSame('bar', $value);
67+
68+
$memoizedCache->shouldHaveReceived('remember')->once()->with('key', 60, Mockery::type(Closure::class));
69+
}
70+
71+
public function test_cache_spy_tracks_remember_calls_on_memoized_cache()
72+
{
73+
$cache = Cache::spy();
74+
75+
$memoizedCache = Cache::memo();
76+
$memoizedCache->remember('key', 60, fn() => 'bar');
77+
78+
$memoizedCache->shouldHaveReceived('remember')->once()->with('key', 60, Mockery::type(Closure::class));
79+
}
80+
81+
public function test_cache_spy_memo_returns_spied_repository()
82+
{
83+
$cache = Cache::spy();
84+
85+
$memoizedCache = Cache::memo();
86+
87+
$this->assertInstanceOf(LegacyMockInterface::class, $memoizedCache);
88+
89+
$memoizedCache->remember('key', 60, fn() => 'bar');
90+
91+
$memoizedCache->shouldHaveReceived('remember')->once();
92+
}
93+
}
94+

0 commit comments

Comments
 (0)