Create Mocks for API Clients in Laravel

• 1 min read

When I work with APIs, I usually create a single PHP class which is responsible for sending the Guzzle HTTP request. It usually looks like this:

namespace App\Services\IMDB;

use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;

class HttpClient
{
    public function send(array $payload) : Response
    {
        return app(Client::class)
            ->request('POST', 'https://api.example.com/movies', $payload);
    }
}

I don't write Unit Tests for such a simple class, but I will write Integration Tests to cover the feature, which will trigger the class in some way or another.

When I want to test how my app reacts to different responses or just to make sure the HttpClient always returns the same response, I mock the HttpClient.

Laravel makes this really easy with the $this->mock() helper (Thanks Taylor).

use GuzzleHttp\Psr7\Response;
use App\Services\IMDB\HttpClient;
use Illuminate\Support\Facades\File;

$mock = $this->mock(HttpClient::class);
$mock->shouldReceive('send')
    ->andReturn(new Response(
        $status = 200,
        $headers = [],
        File::get(base_path('tests/stubs/success.json'))
    ));

This code snippet will create a new Mock for HttpClient and will instruct the Mock to return an instance of GuzzleHttp\Psr7\Response when send is called. The content of success.json is an example response I've once received from the real API.

My Integration Test will then look similar to this:

use GuzzleHttp\Psr7\Response;
use App\Services\IMDB\HttpClient;
use Illuminate\Support\Facades\File;

/** @test */
public function it_updates_a_movie_with_data_from_imdb()
{
    // success.json
    // {
    //     "title": "Skyfall",
    //     "release_date": "2012-10-27",
    //     "cover": "https://example.org/movies/skyfall/cover.jpg"
    // }

    $mock = $this->mock(HttpClient::class);
    $mock->shouldReceive('send')
        ->andReturn(new Response(
            $status = 200,
            $headers = [],
            File::get(base_path('tests/stubs/imdb-responses/success.json'))
        ));

    $response = $this->post('/movies', [
        'title' => 'Skyfall'
    ]);

    $this->assertDatabaseHas('movies', [
        'title' => 'Skyfall',
        'release_date' => '2012-10-27'
    ]);
}

I use this all the time, but always forget the details and have to search in old projects for code snippets. 🤷