DEV Community

Cover image for Copy-Pasting HTTP Fakes by Hand? Laravel Http Automock Can Do It For You
Michael Ruf
Michael Ruf

Posted on

Copy-Pasting HTTP Fakes by Hand? Laravel Http Automock Can Do It For You

If you've spent any time testing Laravel applications that talk to external APIs, you know the drill. You make a real request once, copy-paste the JSON response into a Http::fake(), and hope it doesn't go stale.

I built this tool to eliminate that workflow entirely:

GitHub logo michiruf / laravel-http-automock

Automatically mock http requests when testing

Laravel Http Automock

Run Tests Latest Version on Packagist Total Downloads

Automatically record and replay HTTP responses in your Laravel tests. On the first test run, real HTTP requests are made and the responses are saved to disk. On subsequent runs, the saved responses are used instead, no real requests are made. This makes your tests faster, deterministic, and independent of external services. Automock is fully compatible with Http::fake().

Requires PHP 8.2+, Laravel 11+, and Pest Works only with Laravel's HTTP client.

Quick Start

Installation

composer require michiruf/laravel-http-automock --dev
Enter fullscreen mode Exit fullscreen mode

Optionally publish the config:

php artisan vendor:publish --tag="http-automock-config"
Enter fullscreen mode Exit fullscreen mode

Usage

Call Http::automock() inside your test before executing HTTP requests. Responses will be automatically saved on the first run and replayed on subsequent runs.

it('can do stuff with the api', function () {
    Http::automock()
    $response = Http::get('https://api.sampleapis.com/coffee/hot')->json();
    expect(
Enter fullscreen mode Exit fullscreen mode

The Problem

Testing code that depends on external HTTP services is awkward. You've got three options, and none of them are great:

Hit the real API every time. Your tests are slow, flaky, and dependent on a network connection. Run them in CI and pray the third-party doesn't rate-limit you.

Write Http::fake() manually. You capture a response once, hardcode it, and now you're responsible for keeping it in sync with reality forever. This is easy to forget, and when the API changes, your tests still pass but they're just wrong.

Write nothing. Skip testing that layer altogether. Not ideal.

Http Automock takes a different approach, one that feels a lot like snapshot testing. On the first run it makes the real request and saves the response to disk. Every subsequent run replays the saved response.

How It Works

Install the package as a dev dependency:

composer require michiruf/laravel-http-automock --dev
Enter fullscreen mode Exit fullscreen mode

Then add a single line to your test:

it('fetches hot coffees from the API', function () {
    Http::automock();

    $response = Http::get('https://api.example.com/coffee/hot')->json();

    expect($response)->toHaveCount(20);
});
Enter fullscreen mode Exit fullscreen mode

The first time this test runs, it hits the real API and saves the response to a .mock file at a path like:

tests/.pest/automock/Feature/CoffeeTest/it_fetches_hot_coffees_from_the_API/1_GET_4c147242.mock

Every run after that, the mock file is replayed with no network required. The test is fast, deterministic, and still grounded in real data.

Why This Is Better Than Manual Fakes

The key insight is that mock files live in your repository. This means:

  • Git shows you when APIs change. If you renew your mocks and the response diff looks different, that's worth paying attention to.
  • You get real data confidence. The first run is against a live service, so the response structure is guaranteed to be accurate and not something you typed by hand.

Configuration Options Worth Knowing

Http Automock has a set of options to adapt to different workflows. A few I find particularly useful:

Preventing real requests

Use preventUnknownRealRequests() to throw an exception for any request that doesn't have a mock file yet:

Http::automock()->preventUnknownRealRequests();
Enter fullscreen mode Exit fullscreen mode

For maximum strictness, preventRealRequests() blocks everything, even if mocks exist and you're trying to renew them.

You almost certainly don't want your CI pipeline hitting live APIs. Therefore you can also set these flags via cli option when running your tests.

Renewing / pruning stale mocks

When an API changes its response format, run your tests with renew() or prune() to re-fetch everything and overwrite the existing files:

Http::automock()->renew();
Enter fullscreen mode Exit fullscreen mode

Validating mocks against live responses

Want to detect drift without overwriting anything? validateMocks() makes real requests, compares them to the saved files, and fails the test if they differ:

Http::automock()->validateMocks();
Enter fullscreen mode Exit fullscreen mode

Skipping specific requests

Not everything should be mocked. You can exclude requests by URL pattern, HTTP method, or a custom closure:

Http::automock()->skip('*oauth.example.com*', 'auth');
Http::automock()->skipPost();  // Let POST requests through
Enter fullscreen mode Exit fullscreen mode

Customizing file names

The default filename format is 1_GET_4c147242.mock, a counter, method, and URL hash. If you'd rather have human-readable subdirectories, switch to the url_subdirectory resolver:

Http::automock()->resolveFileNameUsing('url_subdirectory');
// Results in: .../api.example.com/coffee/hot.mock
Enter fullscreen mode Exit fullscreen mode

You can also pass a closure for full control:

Http::automock()->resolveFileNameUsing(
    fn (Request $request, bool $forWriting) => "{$request->method()}_{$request->url()}"
);
Enter fullscreen mode Exit fullscreen mode

Persisting response headers

By default, only response bodies are saved. If your code branches on headers like Content-Type or X-RateLimit-Remaining, opt in:

Http::automock()->withHeaders(['Content-Type', 'X-RateLimit-Remaining']);
Http::automock()->withHeaders(); // Save all headers
Enter fullscreen mode Exit fullscreen mode

A Note on Compatibility

Http Automock is fully compatible with Http::fake(). If you have existing tests that mix real and faked requests, it will play nicely with both. You can even use mockHttpFakes() to capture inline fakes into mock files if you're migrating an existing suite.

Requirements: PHP 8.2+, Laravel 11+, and Pest.

Wrapping Up

The core idea is simple: treat external API responses the same way you treat snapshot tests. Record them once, commit them, and replay them. The difference from snapshot tests is that instead of persisting your outputs/expectations, you're persisting inputs from external services.

If you're building with Laravel and Pest and tired of babysitting fake data, give it a try.

Any feedback is welcome.

Check out the Repo on GitHub

Top comments (0)