How To Mock Intervention/Image In A Laravel Application

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;

  1. The Intervention\Image\Facades\Image class that I was trying to mock is a Facade (Should be obvious, it has "Facades" in its namespace). And

  2. Facades extend the base Illuminate\Support\Facades\Facade class, and

  3. The base Illuminate\Support\Facades\Facade class defines its own shouldReceive 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!