docs/testing-guzzle-clients.md
This page covers Guzzle's local testing tools for client code and handler development. Most tests should mock the HTTP layer without sending requests over the internet.
Use MockHandler when your test needs predictable responses, errors, or
response ordering. Combine it with history middleware when your test also needs
to assert the request method, URI, headers, body, or request options that your
code sent.
Use the separate
guzzlehttp/test-server
package only when you need a local HTTP server, usually while developing or
testing a custom handler. Unit tests and most application client tests should
not use the test server.
Avoid remote services in automated tests unless the test is an explicit, opt-in integration test for that service.
When testing HTTP clients, you often need to simulate specific scenarios like returning a successful response, returning an error, or returning specific responses in a certain order. Because unit tests need to be predictable, easy to bootstrap, and fast, hitting an actual remote API is a test smell.
Guzzle provides a mock handler that can be used to fulfill HTTP requests with queued responses, response promises, request-aware callables, or reject them with queued throwables by shifting return values off of a queue.
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Exception\RequestException;
// Create a mock and queue two responses.
$mock = new MockHandler([
new Response(200, ['X-Foo' => 'Bar'], 'Hello, World'),
new Response(202, ['Content-Length' => '0']),
new RequestException('Error Communicating with Server', new Request('GET', 'test'))
]);
$handlerStack = HandlerStack::create($mock);
$client = new Client(['handler' => $handlerStack]);
// The first request is intercepted with the first response.
$response = $client->request('GET', '/');
echo $response->getStatusCode();
//> 200
echo $response->getBody();
//> Hello, World
// The second request is intercepted with the second response.
echo $client->request('GET', '/')->getStatusCode();
//> 202
// Reset the queue and queue up a new response
$mock->reset();
$mock->append(new Response(201));
// As the mock was reset, the new response is the 201 CREATED,
// instead of the previously queued RequestException
echo $client->request('GET', '/')->getStatusCode();
//> 201
When no more responses are in the queue and a request is sent, an
OutOfBoundsException is thrown.
Queued callables receive the Psr\Http\Message\RequestInterface and request
options array passed to the mock handler. They may return a
Psr\Http\Message\ResponseInterface, a
GuzzleHttp\Promise\PromiseInterface<Psr\Http\Message\ResponseInterface, mixed>,
or a throwable rejection reason.
The on_headers request option is invoked when a queued response or fulfilled
response promise is available. It is not invoked for queued throwables or
rejected promises.
The optional MockHandler constructor callbacks are invoked with the fulfilled
response or rejected reason after the queued value has settled.
When using things like the Mock handler, you often need to know if the
requests you expected to send were sent exactly as you intended. While the mock
handler responds with mocked responses, the history middleware maintains a
history of the requests that were sent by a client.
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Response;
$container = [];
$history = Middleware::history($container);
$mock = new MockHandler([
new Response(200, ['Content-Type' => 'application/json'], '{"ok":true}'),
new Response(204),
]);
$handlerStack = HandlerStack::create($mock);
// Add the history middleware to the handler stack.
$handlerStack->push($history);
$client = new Client([
'base_uri' => 'https://example.test',
'handler' => $handlerStack,
]);
$client->request('GET', '/users/123');
$client->request('DELETE', '/users/123');
// Count the number of transactions
echo count($container);
//> 2
// Iterate over the transaction history. Each transaction contains:
// - request: the request that was sent
// - response: the response, or null when the transfer was rejected
// - error: the rejection reason, or null when the transfer succeeded
// - options: the request options used for the transfer
foreach ($container as $transaction) {
echo $transaction['request']->getMethod();
//> GET, DELETE
if ($transaction['response']) {
echo $transaction['response']->getStatusCode();
//> 200, 204
} elseif ($transaction['error']) {
echo $transaction['error'];
//> exception
}
var_dump($transaction['options']);
//> dumps the request options of the sent request.
}
Using mock responses is almost always enough when testing a web service client. When implementing custom HTTP handlers, you'll need to send actual HTTP requests in order to sufficiently test the handler. However, a best practice is to contact a local web server rather than a server over the internet.
[!TIP] You almost never need to use this test web server. You should only ever consider using it when developing HTTP handlers. The test web server is not necessary for mocking requests. For that, please use the Mock handler and history middleware.
The test server is distributed separately as
guzzlehttp/test-server.
It is not installed with Guzzle by default. The package provides a Node.js
server that receives requests, returns responses from a queue, and records
received requests for inspection.
See the Test Server Usage documentation for lifecycle, response queuing, request inspection, and shutdown details.