PHP製のHTTP ClientのGuzzleでユニットテストを書いていく方法
PHP製のHTTP Clientライブラリの「Guzzle」を使っていると、ユニットテストをしたくなってくる。
幸いにも、Guzzleはテストを行いやすい内部の仕組みができている。 具体的には、handlerを差し込むことによって、テストが可能になるので、その方法を見ていく。
Guzzleのテストの方法
先ほど説明したように、Guzzleではhandlerを設定することによって、Guzzleのリクエスト時の動作を設定することができる。(デフォルトでは、Curlが動く様になっている)
<?php
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]);
使い方は上記のようにMockHandler
を使ってGuzzleのResponseの配列を指定した後に、HandlerStack::create
でhandlerを作成する。
上記の例で$client->request("GET", "/sample")
等とリクエストを送ると、1回目は
new Response(200, ['X-Foo' => 'Bar'], 'Hello, World')
がレスポンスとして返されて、2回目は
new Response(202, ['Content-Length' => 0])
が返される。
そして最後に、RequestException
が返されるように設定できる。
参考記事: Testing Guzzle Clients — Guzzle Documentation
実際のwebアプリでのテストの方法
では、実際のwebアプリではどの様にテストを行なっていくか。 ここでは具体例として、Twitterのクライアントwebアプリを取り扱う。
この手のアプリでは、以下のようなクラスが用意されているはずだ。
<?php
use GuzzleHttp\Client;
class TwitterApiClient
{
public function requestUser(string $twitterUserName): object
{
$client = new Client();
$res = $client->request("GET", "/users/get/{$twitterUserName}", [
"headers" => []
]);
return json_encode($res->getBody());
}
}
コードはかなり適当だが、このようなコードはたくさんある。
ここでテストのために上記のコードに以下のstaticメソッドを追加し、requestUser
メソッドを少し変更する。
<?php
use GuzzleHttp\Client;
class TwitterApiClient
{
protected static $handler;
public static function mockGuzzleHandler($handler)
{
self::$handler = $handler;
}
public function requestUser(string $twitterUserName): object
{
$client = new Client();
$options = [
"headers" => []
];
if(isset(self::$handler)) $options["handler"] = self::$handler;
$res = $client->request("GET", "/users/get/{$twitterUserName}", $options);
return json_encode($res->getBody());
}
}
そしてテストコードを書くときには、以下のようにhandlerを差し込んでやる。
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use TestCase;
class SampleTest extends TestCase
{
public function testGuzzle()
{
$mock = new MockHandler([
// /data/get_user.jsonにtwitterAPIのレスポンスと似たようなjsonデータを用意しておく。
new Response(200, [], file_get_contents(__DIR__ . "/data/get_user.json")),
]);
$handler = HandlerStack::create($mock);
TwitterApiClient::mockGuzzleHandler($handler);
// テストを実行
$this->get(route("twitter.index"));
// 以下は結果の検証を行う。
}
}
これでGuzzleのテストができる。他にもやり方は色々あると思うが、この方法だとGuzzleのリクエスト部分だけ Mockを差し込むことになるので、リクエストの関数全体をMockでするよりも良いテストになるだろう。