Auto-Generating Test Fixtures from Real API Responses with Guzzle Middleware

πŸ“– 3 minutes read

Auto-Generating Test Fixtures from Real API Responses with Guzzle Middleware

When integrating with external APIs, you need test fixtures. Manually crafting them is tedious and error-prone. Here’s a better way: use Guzzle middleware to automatically dump real API responses to files.

The Problem

You’re building a service provider for a third-party API. You need:

  • Realistic JSON fixtures for unit tests
  • Coverage of all API endpoints you use
  • Responses that match the actual API structure (not guesswork)

Manually creating these is painful. The API response structure might have dozens of fields, nested objects, edge cases you haven’t seen yet.

The Solution: Temporary Dumper Middleware

Add a Guzzle middleware to your HTTP client that intercepts responses and saves them to disk:

use GuzzleHttp\HandlerStack;
use Illuminate\Support\Facades\Storage;

// In your service provider's register/boot method
$http = $this->app->make(HttpClientFactory::class)->make($logger);

/** @var HandlerStack $handler */
$handler = $http->getConfig('handler');

$handler->push(function (callable $next) {
    return function ($request, array $options) use ($next) {
        return $next($request, $options)->then(function ($response) use ($request) {
            // Build organized directory structure based on endpoint
            $path = trim($request->getUri()->getPath(), '/');
            $dir = base_path("temp/api-fixtures/{$path}");

            if (!is_dir($dir)) {
                mkdir($dir, 0755, true);
            }

            // Filename based on request body (for POST) or 'get' for GET requests
            $body = (string) $request->getBody();
            $filename = $body ? md5($body) : 'get';

            // Decode, pretty-print, and save
            $json = json_decode((string) $response->getBody(), true);
            file_put_contents(
                "{$dir}/{$filename}.json",
                json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
            );

            // IMPORTANT: Rewind the stream so the original code can still read it
            $response->getBody()->rewind();

            return $response;
        });
    };
}, 'fixture_dumper');

How It Works

Guzzle’s middleware system uses a chain-of-responsibility pattern. This middleware:

  1. Intercepts the response after the HTTP request completes
  2. Extracts the endpoint path from the request URI
  3. Creates an organized directory structure mirroring your API (e.g., temp/api-fixtures/users/profile/)
  4. Names files by request body hash (or ‘get.json’ for GET requests)
  5. Saves pretty-printed JSON to disk
  6. Rewinds the response body so your actual code can still read it

Example Output

After running your integration locally, you’ll have a fixture library like:

temp/api-fixtures/
β”œβ”€β”€ products/
β”‚   β”œβ”€β”€ get.json
β”‚   └── abc123.json (POST /products with specific body)
β”œβ”€β”€ orders/
β”‚   β”œβ”€β”€ get.json
β”‚   β”œβ”€β”€ def456.json
└── users/
    └── profile/
        └── get.json

Each file contains the exact JSON structure returned by the real API.

Using The Fixtures In Tests

Copy the generated fixtures to your test directory:

// tests/Fixtures/ExternalApi/products/get.json
// tests/Fixtures/ExternalApi/orders/get.json

// In your tests:
$mockClient = new MockHandler([
    new Response(200, [], file_get_contents(
        __DIR__ . '/../Fixtures/ExternalApi/products/get.json'
    )),
]);

$this->app->instance(ApiClient::class, new ApiClient(
    new Client(['handler' => HandlerStack::create($mockClient)])
));

When To Remove It

This is temporary development tooling, not production code. Remove the middleware once you have your fixtures:

// ❌ Remove before committing
$handler->push(function (callable $next) { ... }, 'fixture_dumper');

Or make it conditional:

if (config('app.debug') && config('api.dump_fixtures')) {
    $handler->push($fixtureD umperMiddleware, 'fixture_dumper');
}

Benefits

  • Realistic test data – Exact structure from the real API
  • Comprehensive coverage – Hit every endpoint once, get perfect fixtures
  • No manual JSON crafting – Let the API do the work
  • Update fixtures easily – Re-run with middleware enabled when API changes
  • Organized by endpoint – Easy to find the fixture you need

The Takeaway

Guzzle middleware isn’t just for adding auth headers or logging. It’s a powerful tool for development workflows. Use it to auto-generate test fixtures from real API responses, then remove it before production. Your tests get realistic data, and you save hours of manual JSON wrangling.

Daryle De Silva

VP of Technology

11+ years building and scaling web applications. Writing about what I learn in the trenches.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *