In January, Miguel Romero wrote a great article showing how to get started with Guzzle. If you missed the article, or are unfamiliar with Guzzle:

Guzzle is a PHP HTTP client & framework for building RESTful web service clients.

In Miguel’s article, he covered a range of the library’s features, including basic configuration for GET and POST requests, plugins, such as logging using monolog, and finished up with interacting with an OAuth service, specifically GitHub’s API.

In this tutorial, I want to show you how to use Guzzle from a different perspective, specifically how to do unit testing with it. To do this, we’re going to look at three specific approaches:

  1. Hand Crafting Custom Responses
  2. Using a ServiceClient with Mock Response Files
  3. Enqueueing a Server with Mock Responses

Getting Set Up

Note: The source code for this article is available on Github.

Like all things, we need to walk before we can run. In this case, we need to set up our test environment and test class. To get everything in place, we’ll be using Composer. If you’re not familiar with Composer, please read Alexander’s article here on SitePoint before continuing.

Our composer.json file will look as follows:

{
    "require": {
        "php": ">=5.3.3",
    }
    "require-dev": {
        "phpunit/phpunit": "4.0.*",
        "guzzle/guzzle": "~3.7"
    }
}

I’ve stipulated a minimum PHP version of 5.3.3. To be fair, it likely should be higher, but this is a good start. Our only other requirements are PHPUnit and Guzzle. After adding these to composer.json, in your project run composer install and after a short wait, the dependencies will be ready to go.

Preparing PHPUnit

Before we can run our unit tests, we need to do a bit of preparation there as well. First, create a directory in your project called tests. In there, create two files: bootstrap.php and phpunit.xml.dist.

bootstrap.php is quite simple:

<?php
error_reporting(E_ALL | E_STRICT);
require dirname(__DIR__) . '/vendor/autoload.php';

This includes the auto-generated autoload.php file from the vendor directory, which Composer created. It ensures we have access to both PHPUnit and Guzzle. Next, let’s look at phpunit.xml.dist.

<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./bootstrap.php" colors="true">
    <testsuites>
        <testsuite name="importer-tests">
            <directory suffix="Test.php">./</directory>
        </testsuite>
    </testsuites>
</phpunit>

This is a rather rudimentary configuration. What it does is:

  • Tell PHPUnit to use bootstrap.php to bootstrap the test environment
  • Use colors in the test output (handy for discerning the import aspects)
  • Set up one testsuite, called tests. This looks for tests in all files that end in Test.php, located anywhere under the current directory

There is quite an array of available options and configurations, but this suits our needs. If you’re interested, check out the configuration documentation for more details.

With that done, let’s start building our test class. In the tests directory, create a new file called SitePointGuzzleTest.php. In it, add the following:

<?php
use Guzzle\Tests\GuzzleTestCase,
    Guzzle\Plugin\Mock\MockPlugin,
    Guzzle\Http\Message\Response,
    Guzzle\Http\Client as HttpClient,
    Guzzle\Service\Client as ServiceClient,
    Guzzle\Http\EntityBody;

class SitePointGuzzleTest extends GuzzleTestCase 
{
    protected $_client;
}

Here, we’ve imported the key classes which our test class needs. Our class extends from GuzzleTestCase, giving it access to some of the Guzzle-specific test functionality we’ll be using. So far, so good. Let’s look at custom responses.

Hand Crafting Custom Responses

From Miguel’s article, you’ll be familiar with initializing a client to make a request, potentially passing in parameters, then inspecting the response. Let’s assume you’ve created a class which either uses or wraps a Guzzle client, or uses a Guzzle Response object.

And let’s say you’re working with the FreeAgent API and you want to test code which retrieves invoice data.

You want to be sure that your code reacts as required, should something go wrong or the API change. Let’s look at how to mock a response, by going through some annotated code.

public function testAnotherRequest() {
    $mockResponse = new Response(200);
    $mockResponseBody = EntityBody::factory(fopen(
        './mock/bodies/body1.txt', 'r+')
    );
    $mockResponse->setBody($mockResponseBody);
    // ...
}

Here we first instantiate a new Response object. We then use the factory method of Guzzle\Http\EntityBody to set the response body with the contents of the file ./mock/bodies/body1.txt.

This both makes it easy to separate configuration from code and makes test maintenance simpler. body1.txt is available in the source code for this article.

$mockResponse->setHeaders(array(
    "Host" => "httpbin.org",
    "User-Agent" => "curl/7.19.7 (universal-apple-darwin10.0) libcurl/7.19.7 OpenSSL/0.9.8l zlib/1.2.3",
    "Accept" => "application/json",
    "Content-Type" => "application/json"
));

We then pass an associative array to setHeaders, which will set four custom headers on our response.

$plugin = new MockPlugin();
$plugin->addResponse($mockResponse);
$client = new HttpClient();
$client->addSubscriber($plugin);

Here we create a new MockPlugin object, passing in the response object to it. Once we instantiate our new HttpClient, we add the MockPlugin as a subscriber, which in turn will ensure requests made with it use the mock response we’ve just created.

$request = $client->get(
    'https://api.freeagent.com/v2/invoices'
);
$response = $request->send();

Now, as in Miguel’s article, we call get on the client, and send on the returned request to get a response object. With that, we can run a series of test assertions.

$this->assertEquals(200, $response->getStatusCode());
$this->assertTrue(in_array(
    'Host', array_keys($response->getHeaders()->toArray())
));
$this->assertTrue($response->hasHeader("User-Agent"));
$this->assertCount(4, $response->getHeaders());
$this->assertSame(
    $mockResponseBody->getSize(), 
    $response->getBody()->getSize()
);
$this->assertSame(
    1, 
    count(json_decode($response->getBody(true))->invoices
));

In the assertions, you can see that I’ve checked the response code, if Host and User-Agent were in the response headers, the number of headers sent, that the size of the body matched the size of our faked response body and that there was one invoice in the response received.

This is just a small sample of the kinds of tests which can be run, but it shows just how easy it is to both mock a custom response and to test it when it’s retrieved. What other kinds of tests would you run?

Using a ServiceClient with Mock Response Files

That was the long way to mock up a response. If you remember, I emphasised at the start that our test class extends GuzzleTestCase, to get access to some excellent testing functionality. Let’s look at how we can skip a lot of the work we just did by using it.

This time, let’s override setUp as follows:

public function setUp()
{
    $this->_client = new ServiceClient();
    $this->setMockBasePath('./mock/responses');
    $this->setMockResponse(
        $this->_client, array('response1')
    );
}

Here, we’ve instantiated the class variable $_client as a new ServiceClient. We’ve then used setMockBasePath, to ./mock/responses, and called setMockResponse, passing in our client object and an array.

The array lists the names of files in ‘./mock/responses’. The contents of these files will be used to set up a series of responses, which the client will receive for each successive call to send.

In this case, I’ve only added one, but you could easily add as many as you like. You can see in ./mock/responses/response1, that it lists the HTTP version, status code, headers and response body. Once again, it keeps the code and configuration neatly separate.

Now, let’s look at the function which uses it.

public function testRequests() {
    $request = $this->_client->get(
        'https://api.freeagent.com/v2/invoices'
    );
    $request->getQuery()->set(
        'view', 'recent_open_or_overdue'
    );
    $response = $request->send();

    $this->assertContainsOnly(
        $request, $this->getMockedRequests()
    );
    $this->assertEquals(200, $response->getStatusCode());
    $this->assertEquals(
        'AmazonS3', $response->getServer()
    );
    $this->assertEquals(
        'application/xml', $response->getContentType()
    );
}

You can see that all I’ve had to do is to make a call to get on the client object and send on the returned request object, as we normally would. I’ve added in query parameters just for good measure.

As before, I’ve then been able to run a series of assertions, checking the mocked requests, returned status code, server and content-type header.

One thing worth mentioning is that it’s a FIFO queue; meaning that the first response added is the first which will be sent. Don’t let this trip you up.

Enqueueing a Server with Mock Responses

Finally, let’s look at one of the cooler aspects of Guzzle. If we call $this->getServer()->enqueue(array());, Guzzle transparently starts a node.js server behind the scenes. We can then use that to send requests to, as if it was our real server endpoint. Let’s have a look at how to use it.

public function testWithRemoteServer() {
    $mockProperties = array(
        array(
            'header' => './mock/headers/header1.txt',
            'body' => './mock/bodies/body1.txt',
            'status' => 200
        )
    );
}

Here I create an array to store header, body, and status information for a mock request, specifying a status code of 200, and files containing the header and body response data.

$mockResponses = array();

foreach($mockProperties as $property) {
    $mockResponse = new Response($property['status']);
    $mockResponseBody = EntityBody::factory(
        fopen($property['body'], 'r+')
    );
    $mockResponse->setBody($mockResponseBody);
    $headers = explode(
        "\n", 
        file_get_contents($property['header'], true)
    );
    foreach($headers as $header) {
        list($key, $value) = explode(': ', $header);
        $mockResponse->addHeader($key, $value);
    }
    $mockResponses[] = $mockResponse;
}

Then I’ve created a new Response object, setting the status code, and again used the factory method of the EntityBody class to set the body. The headers were a bit more cumbersome, so I’ve iterated over the contents of the file, calling addHeader for each key/value pair retrieved.

$this->getServer()->enqueue($mockResponses);

Each mock response object created is added to an array, which is then passed to enqueue. Now it has a set of responses ready to be sent to our client requests.

$client = new HttpClient();
$client->setBaseUrl($this->getServer()->getUrl());
$request = $client->get();
$request->getQuery()->set(
    'view', 'recent_open_or_overdue'
);
$response = $request->send();

Here, as before, we’ve initialized our client object, calling get and send on the returned request. The one thing to note, is that to use the Node.JS server, we need to pass $this->getServer()->getUrl() to $client->setBaseUrl(), otherwise it won’t work.

$this->assertCount(5, $response->getHeaders());
$this->assertEmpty($response->getContentDisposition());
$this->assertSame('HTTP', $response->getProtocol());

Following that, our code works as it did before, and I’ve added assertions for the headers, content disposition header and the protocol in the returned response object.

Wrapping Up

So, what do you think? Though it does take a bit of work, I’m sure you can see how simple it is to set up the different response approaches. I’m sure with a little work, you could also simplify the approaches I’ve taken.

I know that this only scratched the surface of what’s possible with testing in Guzzle, but I hope I piqued your curiosity, showing you a wealth of ways to ensure that your client code is fully test covered.

What’s your experience? Share your thoughts in the comments.

Frequently Asked Questions (FAQs) about Unit Testing with GuzzlePHP

How can I mock a response in GuzzlePHP for unit testing?

Mocking a response in GuzzlePHP is a crucial part of unit testing. It allows you to simulate a specific response from an HTTP request without actually making a call to the server. To do this, you can use the MockHandler class provided by Guzzle. This class accepts an array of responses that it will return in the order they are provided. Here’s a simple example:

use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Psr7\Response;

$mock = new MockHandler([
new Response(200, [], 'Hello, World!'),
]);

$handlerStack = HandlerStack::create($mock);
$client = new Client(['handler' => $handlerStack]);

In this example, the client will return a 200 response with ‘Hello, World!’ as the body for the first request it makes.

How can I integrate GuzzlePHP with PHPUnit for testing?

PHPUnit is a popular testing framework for PHP and it can be used in conjunction with Guzzle for unit testing. To do this, you will need to create a test case class that extends the PHPUnit\Framework\TestCase class. Within this class, you can use Guzzle to make HTTP requests and PHPUnit to assert the responses. Here’s a simple example:

use PHPUnit\Framework\TestCase;
use GuzzleHttp\Client;

class MyTest extends TestCase
{
public function testGet()
{
$client = new Client();
$response = $client->get('http://httpbin.org/get');

$this->assertEquals(200, $response->getStatusCode());
$this->assertJson($response->getBody()->getContents());
}
}

In this example, a GET request is made to ‘http://httpbin.org/get‘ and the status code and body of the response are asserted.

How can I handle exceptions during unit testing in GuzzlePHP?

When performing unit tests, it’s important to handle exceptions properly to ensure your tests are robust and reliable. In Guzzle, exceptions can be handled using try-catch blocks. For example:

use GuzzleHttp\Exception\RequestException;

try {
$client = new Client();
$response = $client->get('http://httpbin.org/status/500');
} catch (RequestException $e) {
echo Psr7\Message::toString($e->getRequest());
if ($e->hasResponse()) {
echo Psr7\Message::toString($e->getResponse());
}
}

In this example, a GET request is made to a URL that will return a 500 status code. The RequestException is caught and the request and response are printed.

How can I test asynchronous requests in GuzzlePHP?

Testing asynchronous requests in Guzzle can be a bit tricky, but it’s definitely possible. You can use the then method provided by the Promise class to assert the response once it’s available. Here’s an example:

use GuzzleHttp\Promise;

$promise = $client->getAsync('http://httpbin.org/get');
$promise->then(
function (Response $res) {
$this->assertEquals(200, $res->getStatusCode());
},
function (RequestException $e) {
$this->fail($e->getMessage());
}
);

$promise->wait();

In this example, a GET request is made asynchronously to ‘http://httpbin.org/get‘. The then method is used to assert the status code of the response once it’s available.

How can I test the body of a response in GuzzlePHP?

Testing the body of a response is a common requirement in unit tests. In Guzzle, you can use the getBody method to get the body of a response as a stream. You can then convert this stream to a string using the getContents method. Here’s an example:

$client = new Client();
$response = $client->get('http://httpbin.org/get');

$body = $response->getBody();
$content = $body->getContents();

$this->assertJson($content);

In this example, a GET request is made to ‘http://httpbin.org/get‘ and the body of the response is asserted to be JSON.

How can I test the headers of a response in GuzzlePHP?

Testing the headers of a response is another common requirement in unit tests. In Guzzle, you can use the getHeaders method to get an associative array of headers. You can then assert these headers as needed. Here’s an example:

$client = new Client();
$response = $client->get('http://httpbin.org/get');

$headers = $response->getHeaders();

$this->assertArrayHasKey('Content-Type', $headers);
$this->assertEquals('application/json', $headers['Content-Type'][0]);

In this example, a GET request is made to ‘http://httpbin.org/get‘ and the ‘Content-Type’ header of the response is asserted.

How can I test POST requests in GuzzlePHP?

Testing POST requests in Guzzle is similar to testing GET requests. You can use the post method to make a POST request and then assert the response as needed. Here’s an example:

$client = new Client();
$response = $client->post('http://httpbin.org/post', [
'form_params' => [
'field' => 'value',
],
]);

$this->assertEquals(200, $response->getStatusCode());

In this example, a POST request is made to ‘http://httpbin.org/post‘ with some form parameters and the status code of the response is asserted.

How can I test file uploads in GuzzlePHP?

Testing file uploads in Guzzle can be done using the multipart option. This option allows you to specify an array of multipart form data. Here’s an example:

$client = new Client();
$response = $client->post('http://httpbin.org/post', [
'multipart' => [
[
'name' => 'file',
'contents' => fopen('/path/to/file', 'r'),
],
],
]);

$this->assertEquals(200, $response->getStatusCode());

In this example, a POST request is made to ‘http://httpbin.org/post‘ with a file and the status code of the response is asserted.

How can I test redirects in GuzzlePHP?

Testing redirects in Guzzle can be done using the allow_redirects option. This option allows you to specify whether redirects should be followed or not. Here’s an example:

$client = new Client([
'allow_redirects' => false,
]);
$response = $client->get('http://httpbin.org/redirect/1');

$this->assertEquals(302, $response->getStatusCode());

In this example, a GET request is made to a URL that will redirect. The allow_redirects option is set to false, so the status code of the response is 302.

How can I test cookies in GuzzlePHP?

Testing cookies in Guzzle can be done using the cookies option. This option allows you to specify a CookieJar that will hold the cookies. Here’s an example:

use GuzzleHttp\Cookie\CookieJar;

$jar = new CookieJar();
$client = new Client([
'cookies' => $jar,
]);
$response = $client->get('http://httpbin.org/cookies/set?name=value');

$this->assertTrue($jar->hasCookieNamed('name'));

In this example, a GET request is made to a URL that will set a cookie. The cookies option is set to a CookieJar, so the cookie can be asserted.

Matthew SetterMatthew Setter
View Author

Matthew Setter is a software developer, specialising in reliable, tested, and secure PHP code. He’s also the author of Mezzio Essentials (https://mezzioessentials.com) a comprehensive introduction to developing applications with PHP's Mezzio Framework.

freeagentguzzleguzzlephpmockingnodejsPHPphpunitresponsesunit testing
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form