Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache::get() can't be mocked #10803

Closed
tillkruss opened this issue Nov 4, 2015 · 14 comments
Closed

Cache::get() can't be mocked #10803

tillkruss opened this issue Nov 4, 2015 · 14 comments

Comments

@tillkruss
Copy link
Collaborator

Issue #9729 and #10480 have been closed without fix or explanation.

The code example from the official "Testing" docs not work: http://laravel.com/docs/5.1/testing#mocking-facades

Either the official docs need to be updated, or the Cache facade adjusted to make it work.

Cache::shouldReceive('get')
   ->once()
   ->with('key')
   ->andReturn('value');

Throws:

InvalidArgumentException: get() cannot be mocked as it a protected method and mocking protected methods is not allowed for this mock
@GrahamCampbell
Copy link
Member

That's because you don't need to mock the get method on the cache repository, because there isn't one to mock.

@GrahamCampbell
Copy link
Member

Yes, the docs are totally wrong here, sorry. We're open to PRs though. :)

@GrahamCampbell
Copy link
Member

Issue #9729 and #10480 have been closed without fix or explanation.

Well that's totally untrue.

I added a reason straight away.

You can't mock a manager class like that I'm afraid. The get call there is a __callStatic followed by a __call. You can only mock facades if the next call from __callStatic is a real method.

@taylorotwell
Copy link
Member

I've fixed this in laravel/framework by instructing mockery to allow mocking protected methods on the Facade base class. Fix should be released on next tag.

@tillkruss
Copy link
Collaborator Author

Thanks! 👍

@apsdsm
Copy link

apsdsm commented Nov 5, 2015

Was just hitting this problem - thanks for fixing :)

@jkatz50
Copy link

jkatz50 commented Mar 17, 2016

Using laravel 5.2 and still having issues with Mockery Cache:
BadMethodCallException: Method Mockery_1_Illuminate_Cache_CacheManager::put() does not exist on this mock object

@tillkruss
Copy link
Collaborator Author

That's a different error message.

@priyanshuchhazed
Copy link

priyanshuchhazed commented Dec 16, 2016

I'm facing a similar issue with Mockery Cache:
BadMethodCallException: Method Mockery_0_Illuminate_Cache_CacheManager::driver() does not exist on this mock object

@GrahamCampbell
Copy link
Member

It's not a bug as such, it's because you're being naive with the mocking. The cache manager is only a proxy, so you can't mock methods that you think exist, because they don't. You'll need to mock the __call method, or, mock the underlying "cache repository instance".

@richartkeil
Copy link

richartkeil commented Jul 1, 2018

To provide a quick solution for the described problem of @priyanshuchhazed (even if it's not issue related and quite some time ago):

In a normal Unit test mocking through Cache::shouldReceive('...') works perfectly fine.

However if you write an integration test faking an HTTP request you need to condition the mock like this:

$cacheDriver = app('cache')->driver();
Cache::shouldReceive('driver')->andReturn($cacheDriver);

@sergsoares
Copy link

sergsoares commented Nov 27, 2018

To provide a quick solution for the described problem of @priyanshuchhazed (even if it's not issue related and quite some time ago):

In a normal Unit test mocking through Cache::shouldReceive('...') works perfectly fine.

However if you write an integration test faking an HTTP request you need to condition the mock like this:

$cacheDriver = app('cache')->driver();
Cache::shouldReceive('driver')->andReturn($cacheDriver);

Work perfectly for me. Thanks

@hettiger
Copy link
Contributor

hettiger commented May 7, 2020

It's not a bug as such, it's because you're being naive with the mocking. The cache manager is only a proxy, so you can't mock methods that you think exist, because they don't. You'll need to mock the __call method, or, mock the underlying "cache repository instance".

Here's what I've came up with reading your comment:

// arrange
$cacheRepository = Cache::driver();
$cacheRepositorySpy = Mockery::spy($cacheRepository);
Cache::swap($cacheRepositorySpy);

// act
$this->post(/* … */);

// assert
$cacheRepositorySpy->shouldHaveReceived('has');
$cacheRepositorySpy->shouldHaveReceived('get');

– or –

$partialCacheMock = Mockery::mock(Cache::driver())->makePartial();
$partialCacheMock->shouldReceive('put')->once();
Cache::swap($partialCacheMock);

Maybe this is helpful for others that come across this issue…

Actually it seems to be working just fine. I'm wondering if it would be a good idea to make Cache::spy() return the equivalent of my $cacheRepositorySpy from the example above.

It took me hours to figure this out. Documentation and / or API clearly needs an improvement to be honest.

@hettiger
Copy link
Contributor

Somehow I keep coming back to this and I'm always happy that I left this comment for others and future self. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants