Image manipulation can be time-consuming and can eat up system resources. So, when writing tests for your application, mocking the class or service that handles this task is definitely a smart move.
In my case, that class or service is provided by Intervention/Image, a great PHP package that handles image manipulation really well and integrates well with Laravel - my PHP framework of choice.
Mocking in Laravel is very easy, but without understanding some of the framework's architecture concepts like the Service container and Facades, it's as easy to find yourself in a situation where things are just not working as you'd expect them to.
I found myself in such a situation recently, when I tried to mock the Intervention\Image\Facades\Image
class' make
method as follows;
use Intervention\Image\Facades\Image;
use Mockery\MockInterface;
$mock = $this->mock(Image::class, function (MockInterface $mock) {
$mock->shouldReceive('make')->once()->andReturnSelf();
});
I was sure this was going to work because it looks very similar to the example code snippet in the Laravel documentation on Mocking Objects.
But when I ran my test, I go the following error;
Fatal error: Cannot redeclare Mockery_2_Intervention_Image_Facades_Image::shouldReceive() in project/vendor/mockery/mockery/library/Mockery/Loader/EvalLoader.php(34) : eval()'d code on line 1004
Fatal error: Uncaught Illuminate\Contracts\Container\BindingResolutionException: Target [Illuminate\Contracts\Debug\ExceptionHandler] is not instantiable. in project/vendor/laravel/framework/src/Illuminate/Container/Container.php:1089
...
Aghhh!! Why!!!!???
Well, after doing some digging, here's what I learned;
The
Intervention\Image\Facades\Image
class that I was trying to mock is a Facade (Should be obvious, it has "Facades" in its namespace). AndFacades extend the base
Illuminate\Support\Facades\Facade
class, andThe base
Illuminate\Support\Facades\Facade
class defines its ownshouldReceive
method, which returns an instance of a Mockery mock.
Therefore, just as the error message says, the shouldReceive
method is already declared on the Intervention\Image\Facades\Image
class, so I should instead be able to just call it.
For example, instead of the code I wrote above, mocking the class' make
method should look like this:
use Intervention\Image\Facades\Image;
Image::shouldReceive('make')->once()->andReturnSelf();
And running the test again, sure enough, it works!