背景
PHPUnit を使っていると、関数をモックにしたい場面があります。
一般には下記のように実装します。
Mockery::mock('class name')->shouldReceive('method')->withArgs($param)->andReturn($returnValue);
こういった書き方では返り値が決め打ちになっています。
ほとんどの場面では、返り値は固定値で問題ないはずですが、今目の前で、動的に返り値が決まっていたほうが都合がいい場面が起こったので調べてみました。
andReturnUsing
を利用する
ありました。andReturnUsing
という関数を使えばいいです。
書き方はコールバックを受け取り、コールバックの引数をモック化したい関数の引数と同じにします。
あとはそのコールバックの返り値を計算してreturn
するだけで返り値を指定できます。
Mockery::mock('class name')
->shouldReceive('method')
->withArgs(function ($params1, $params2) {
// $paramsを使った処理・・・
return true;
})
->andReturnUsing(function ($params1, $params2) {
// $paramsを使った処理・・・
return $calculateResult;
}
);
実例
ユースケースの中で、別のエンドポイントに通信して、得た結果で引き続き処理をするというものがありました。この場合は通信自体モックにするのがベターですが、どうしても通信先の処理も結合テストしたかったので、まとめてテストできるようにテストを書きました。
$clientMock = Mockery::mock(Client::class);
$clientMock->shouldReceive('post')
->withArgs(function (string $uri, array $data) {
// 省略
return true;
})->andReturnUsing(function (string $uri, array $data) {
$response = $this->post('{テストしたいエンドポイント}', $data['form_params']);
$mock = Mockery::mock('TestResponse');
// GuzzleのResponse Interfaceをモック
$mock->shouldReceive('getStatusCode')->andReturn(200);
$mock->shouldReceive('getBody')->andReturn(json_encode($response->json()));
return $mock;
});
$this->app->instance(Client::class, $clientMock);
こうすると、andReturnUsing
の中で、$this->post
で実際の通信処理が走るので、別のエンドポイントの動作チェックも兼ねることができます。
また、引数で$data['form_params']
を渡すことで、引数が正しく渡っていることがテストできます。
繰り返しますが、本当はこういった場合は個別にテストを書いたほうがいいですね。