> ## Content Index
> Fetch the complete content index at: https://madewithlove.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Building an SDK with PHP, part 3: Making it testable
- URL: https://madewithlove.com/blog/building-an-sdk-with-php-part-3-making-it-testable/
- Published: 2021-07-27T15:03:05.000Z
- Updated: 2026-05-29T11:57:21.000Z
- Author: Bram Devries
- Tags: Engineering, Architecture, Testing, Tutorials

*This post is part 3 of the “Building an SDK with PHP” series. Read [Part 1](https://madewithlove.com/blog/building-an-sdk-with-php-part-1/) and [Part 2](https://madewithlove.com/blog/building-an-sdk-with-php-part-2-making-it-configurable/)*

In our last article we’ve looked at how we can make our SDK configurable and today we’ll apply this to cover our SDK with several unit tests.

## **What should we test?**

The test suite of our SDK should be focused on the behaviour that it offers. If our SDK offers methods to interact with an HTTP API, then those methods should be tested. In the context of our SDK this means that, given a certain set of parameters, we want to assert the correct HTTP request is being made. However, we do not want our test suite to actually perform an HTTP request.

To solve this, the first thing that comes to mind is that we should mock our HTTP client using [PHPUnit’s mocks](https://phpunit.de/manual/6.5/en/test-doubles.html) or [Mockery](https://docs.mockery.io/en/latest/). The downside of that approach is that this would heavily tie our tests to our implementation. If we decide to refactor the internals of our SDK, we would have to refactor our tests as well.

This is where the [Mock Client](https://docs.php-http.org/en/latest/clients/mock-client.html) package comes in. This client is essentially an in-memory implementation of an HTTP Client that collects the requests that are performed but also allows faking responses.

## **Mocking our requests**

The first step is to install the Mock Client dependency with composer:

```bash
composer require --dev php-http/mock-client
```

The first test we will write will be a fairly simple one: we will retrieve the current HTTP client on the SDK and try to perform a GET request to a URL.

First of all, we’ll create our own `TestCase` implementation that we can extend with our own methods to make common parts easier to execute. In this case, I’m providing a `givenSdk()` method that will return an SDK instance:

```php
abstract class TestCase extends \PHPUnit\Framework\TestCase
{
    protected function givenSdk(): Sdk
    {
        return new Sdk();
    }
}

```

The second thing we’ll do is make sure we default to use the Mock Client implementation so we do not accidentally send out a real HTTP request. We can configure this in our PHPUnit’s bootstrap file by adding the following lines:

```
use Http\Discovery\ClassDiscovery;
use Http\Discovery\Strategy\MockClientStrategy;

require __DIR__.'/../vendor/autoload.php';

ClassDiscovery::prependStrategy(MockClientStrategy::class);

```

To explain what this does we’ll do a little throwback to part 1 of this series where I explained that PHP-HTTP is able to [discover](https://docs.php-http.org/en/latest/discovery.html) a PSR-18 implementation. The line above makes sure that Mock Client will always be used.

Now that we’ve done preliminary work we can create our first test.

```php
final class SdkTest extends TestCase
{
   public function testCanRequest200Response(): void
    {
        $httpClient = $this->givenSdk()->getHttpClient();
        $response = $httpClient->get('/todos');

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

```

In the test above we’re asserting that the response we get after doing a GET request has a status code of 200, which is the default response returned by Mock Client. This might not always be what you want, but we can leverage Mock Client to customize the response using the `addResponse` method.

In order to do this, we’ll need to have access to our actual `Mock\Client` instance. A simple way to do this is to add it as a property to our base `TestCase` and use that client in our `givenSdk` method. This also means we reduce the boilerplate in our actual test cases.

```php
abstract class TestCase extends \PHPUnit\Framework\TestCase
{
    protected Client $mockClient;

    protected function setUp(): void
    {
        parent::setUp();

        $this->mockClient = new Client();
    }

    protected function givenSdk(): Sdk
    {
        return new Sdk(new Options([
            'client_builder' => new ClientBuilder($this->mockClient),
        ]));
    }
}

```

The actual test would look like this:

```php
public function testCanRequest201Response(): void
{
    $this->mockClient->addResponse((new Response())->withStatus(201));
    $httpClient = $this->givenSdk()->getHttpClient();
    $response = $httpClient->post('/todos');

    $this->assertEquals(201, $response->getStatusCode());
}

```

The mocked response added by `addResponse` will only be used for the first request done by the HTTP client. If you need to assert multiple requests you can add additional responses as seen below.

```php
public function testCanRequestMultiple201Responses(): void
{
    $this->mockClient->addResponse((new Response())->withStatus(201));
    $this->mockClient->addResponse((new Response())->withStatus(201));

    $httpClient = $this->givenSdk()->getHttpClient();

    $this->assertEquals(201, $httpClient->post('/todos')->getStatusCode());
    $this->assertEquals(201, $httpClient->post('/todos')->getStatusCode());
}

```

While this approach would cover a lot of use cases in your tests, there might be cases where you want to have some more control and specific mock responses based on the request. To do this we can make use of the `on` method that takes a `RequestMatcher` and the `Response` as it’s argument.

In the following example we’ll mock the response for a 404 endpoint so that it returns a 404 status code. But we’ll do this only once during our test setup:

```php
public function testCanHandle404s(): void
{
    $this->mockClient->on(new RequestMatcher('404'), (new Response())->withStatus(404));

    $httpClient = $this->givenSdk()->getHttpClient();

    $this->assertEquals(200, $httpClient->get('/todos')->getStatusCode());
    $this->assertEquals(404, $httpClient->get('/404')->getStatusCode());
    $this->assertEquals(404, $httpClient->get('/404')->getStatusCode());
    $this->assertEquals(404, $httpClient->get('/404')->getStatusCode());
}

```

## **Conclusion**

We’ve seen how you can leverage PSR-18 and the php-http/mock-client implementation to improve tests that rely on HTTP requests so that they are more easy to read and maintain. While this was done in the context of building an SDK, you can apply the same techniques to an application.

As always, you can see the current status of this example SDK that we’ve been building on [GitHub](https://github.com/bramdevries/example-php-sdk).