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:
michiruf
/
laravel-http-automock
Automatically mock http requests when testing
Laravel Http Automock
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
Optionally publish the config:
php artisan vendor:publish --tag="http-automock-config"
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(…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
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);
});
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();
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();
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();
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
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
You can also pass a closure for full control:
Http::automock()->resolveFileNameUsing(
fn (Request $request, bool $forWriting) => "{$request->method()}_{$request->url()}"
);
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
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.
Top comments (0)