Whether you're writing a client for your own web API to offer it to users or you're simply implementing integration for a 3rd-party API in your system, it is important to test it to make sure your client is capable of handling actual API responses correctly.
Testing web API clients is mostly about checking how they deal with responses received after sending requests to API endpoints, and for your unit tests, you introduce test doubles to simulate API calls instead of executing real HTTP requests.
Remark
When faced with such and similar challenges, PHP developers usually resort to mocking, facilitated either by PHPUnit or some alternative framework, and methods such as expects()
, with()
, willReturn()
and similar. But this persistent and excessive use of mocking seems very unnatural to me as if there are no alternatives, especially in the case where it is needed to simulate web service requests.
Sadly, mocking has become the predominant term in testing as if there are no other patterns for replacing production object for testing purposes. Almost as googling, like there's no other way to search content on the Internet.
— Nikola Poša (@nikolaposa) April 1, 2018
Ideally, test code should resemble usage examples from a README file, instead of being overwhelmed with impractical directives that make sense only in the testing context.
HTTP layer stub
Web API clients (or SDKs) are typically built on top of some HTTP client implementation that facilitates communication with the API, which can be reduced to a direct usage of cURL functions or some more sophisticated and robust solution. Guzzle is probably the most popular HTTP client for PHP providing a simple and convenient object-oriented interface for executing HTTP requests.
For sending HTTP requests, Guzzle features handlers system, and amongst default handlers there is one called Mock Handler, designed just for the purpose of simulating different successful and error response scenarios without hitting an actual web API. With the help of it, instead of messing with mocking Guzzle and its methods that your API client is invoking during execution, you elegantly setup a response that should be returned when the Guzzle object is used by your API client:
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Psr7\Response;
use My\ApiClient;
use PHPUnit\Framework\TestCase;
class ApiClientTest extends TestCase
{
protected $apiClient;
protected $mockHandler;
protected function setUp()
{
$this->mockHandler = new MockHandler();
$httpClient = new Client([
'handler' => $this->mockHandler,
]);
$this->apiClient = new ApiClient($httpClient);
}
/**
* @test
*/
public function it_retrieves_students_collection()
{
$this->mockHandler->append(new Response(200, [], file_get_contents(__DIR__ . '/fixtures/products.json')));
$products = $this->apiClient->getStudents();
$this->assertCount(5, $products);
}
}
Can you imagine the overhead of using mocking methods for achieving the same goal in some more complex scenario?
The irony, of course, is that the author formulated this handy test double as MockHandler
, instead of StubHandler
, so I'm tempted to create a pull request to fix this inaccurate name because mocks are not stubs.
Comments