テスト対象が外部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
が返っていることが確認できます。