How to Mock Http Response in Laravel

2 months ago

Hi 👋

The mocking with HTTP response in Laravel might not be easy. Today, I will show you how to mock HTTP response with Http Fake.

Why?

Imagine your app trying to download a file from http server. So, in your test, you may download the file every time and do other operations. That means, your test always depends on HTTP responses which makes your test slower. Even, though your test is always required on the internet otherwise HTTP file cannot be downloaded.

Then, how do you handle this?

There would be few options. Probably the most common solution here is Mocking the HTTP response. So, we can control URL response whatever we want.

Service

Let's create a dedicated class for downloading the ISO files.

namespace App\Services;

use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Http;

class IsoFileService
{
    public function downloadLatestFile()
    {
        Http::get('https://www.iso20022.org/sites/default/files/ISO10383_MIC/ISO10383_MIC_NewFormat.csv');

        echo "Download Completed";
    }
}

Now, in the routes/console.php write a custom command to download the ISO file.

use App\Services\IsoFileService;

Artisan::command('iso:download', function () {
    return (new IsoFileService)->downloadLatestFile();
})->describe('Download the ISO file');

Now, run the command in the terminal-

php artisan iso:download

If you run the command, it should download the latest ISO file and store it as tmp-iso-* in the /storage folder.

Testing

Let's create a test for this service by the following command-

php artisan make:test IsoFileDownloadTest

Create a fixture file

Let's download the ISO file via browser and create a fixture file called tests/Fixtures/valid_latest_iso_file.csv. Every time our test tries to download the file via a particular URL, then it returns the created fixture file instead of downloading the fresh file.

Let's write some code-

namespace Tests\Feature;

use BlastCloud\Guzzler\UsesGuzzler;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class IsoFileServiceTest extends TestCase
{
    /** @test */
    public function iso_download_wtih_HTTP_fake()
    {
    // Make the ISO url fake and set a file response
        Http::fake([
            'https://www.iso20022.org/sites/default/files/ISO10383_MIC/ISO10383_MIC_NewFormat.csv' => Http::response(
                file_get_contents('tests/Fixtures/valid_latest_iso_file.csv')
            )
        ]);

    // This response will return a set file INSTEAD of downloading the latest from the ISO server
        $response = Http::get('https://www.iso20022.org/sites/default/files/ISO10383_MIC/ISO10383_MIC_NewFormat.csv');

        $this->assertEquals(200, $response->status());
        $this->assertContains(
            '"DRSP","DRSP","OPRT","EURONEXT  UK - REPORTING SERVICES","EURONEXT LONDON LIMITED","969500HMVSZ0TCV65D58","APPA","","GB","LONDON","WWW.EURONEXT.COM","ACTIVE","20210927","20210927","20210927","","APPROVED PUBLICATION ARRANGEMENT."',
            explode("\n", $response->body())
        );
    }
}

Now, run the test-

vendor/bin/phpunit --filter IsoFileServiceTest

Now the test won't hit the ISO server for downloading the file for testing purposes. It response from the fixture file that we set in Http::fake().

Hope it helps you for understanding Http response mocking in Laravel via HTTP Fake.

Thanks.