テスト対象が外部APIにアクセスしている場合、テストの度にAPIにアクセスするためテストに時間がかかる・APIが落ちているとテストも落ちる、といった不便さがあります。そこでモックを使用すると、APIを介すことなく対象のテストを行うことができて便利です。本記事では、Mockeryを使ったモックの作成方法・テストの書き方をご紹介します。
テスト対象クラス
以下のTestApiがテスト対象です。外部APIを介してデータを取得し、リストを返却。
なんらかのエラーでデータが取得できなかった場合にはfalseを返すというシンプルなものです。
class TestApi
{
private $connection;
public function __construct(TestOAuth $testOAuth) // ← TestAuthがモックの対象
{
$this->connection = $testOAuth;
}
public function getList(string $param)
{
$list = $this->connection->get($param);
//データ取得できなかった場合
if (property_exists($list, 'errors')) {
return false;
}
return $list->statuses;
}
}
また、モック対象は、TestOAuthです。TestApiのコンストラクタでインジェクションされていますね。
Mockeryでモックしてテスト:正常ケース
テストコード全体は以下のようになります。
public function getListTest()
{
//モックを作成
$mock = \Mockery::mock(TestOAuth::class);
$mock->shouldReceive('get')
->once()
->with('正常test')
->andReturn((object) [
'statuses' =>
[
(object)['data1'],
(object)['data2']
]
]);
//テストここから
$testApi = new TestApi($mock);
$actual = $testApi->getList('正常test');
$this->assertCount(2, $actual);
}
モック部分を解説します。
$mock = \Mockery::mock(TestOAuth::class);
まず最初に、mockメソッドでモックインスタンスを作成します。引数にはモック対象のTestOAuthを渡します。
$mock->shouldReceive('get')
->once()
->with('正常test')
->andReturn([
'statuses' =>
[
(object)['data1'],
(object)['data2']
]
]);
次に、作成したモックインスタンスに対してgetメソッドが呼ばれた際の振る舞いを指定しています。
onceは、メソッドが1度だけ呼ばれること。
withは、getメソッドに渡される引数が「正常test」であること。
そして、最後のandReturnではAPIからのレスポンスにあたる部分で、このモックでは2つのオブジェクトを含む配列を持つstatusesプロパティが返るようにしています。
また、モックインスタンスは作成しただけではテストに反映されません。以下のようにコンストラクタに渡すことでTestApiのnew時にDIされ、実際のTestOAuthではなくモックが使用されるようになります。
$testApi = new TestApi($mock);
テストは以下のように、コマンドラインで実行します。
$ vendor/bin/phpunit --filter getListTest PHPUnit 9.5.16 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 00:00.131, Memory: 22.00 MB
無事テストが通ったので、モックに設定したふるまい(getが1度呼ばれる・引数の中身)が正常であったことが確認できました。
Mockeryでモックしてテスト:エラーケース
次は、エラーケースのテストになります。テスト対象メソッドの以下にあたる箇所ですね。
//データ取得できなかった場合
if (property_exists($list, 'errors')) {
return false;
}
APIからのレスポンスにerrorsというプロパティが含まれている場合に、正しくfalseが返るかをテストします。
そのため、モック作成部分では以下のように指定します。
$mock = \Mockery::mock(TestOAuth::class);
$mock->shouldReceive('get')
->once()
->with('エラーtest')
->andReturn(['errors' => []]);
モックオブジェクトの作成やgetが1度呼ばれるという箇所は同じですが、andReturnで返り値にerrorsプロパティを含むように記述しています。
モック部分を含めたテストの全文は以下のようになります。
public function getListTest_error()
{
$mock = \Mockery::mock(TestOAuth::class);
$mock->shouldReceive('get')
->once()
->with('エラーtest')
->andReturn(['errors' => []]);
$testApi = new TestApi($mock);
$actual = $testApi->getList('エラーtest');
$this->assertEquals(false, $actual);
}
これでテストを実行しOKであれば、呼ばれたモックオブジェクトの振る舞いが期待通りかに加え、正しくfalseが返っていることが確認できます。
