概要
前回の記事で紹介しましたが、テストコードでモックを使う際は、Mockeryを使ってます。
開発を進めて、学んだことがあったのでここに残します。
TL;DR
- MockeryのandThrowは第一引数がインスタンスか例外クラスで切り分け
- 第一引数が例外クラスのときは、例外クラスがメッセージ、コードの順じゃないとエラーになる
- etc
AccessDeniedHttpException
本編
やっていたのはLaravelプロジェクトのテスト作成です。
下記のようにクラスの返り値が AccessDeniedHttpException
でときのものです。
他の例外と同様に書いていました。
<?php
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Tests\TestCase;
use Mockery;
class HogeTest extend TestCase
{
public function testHoge($id)
{
// setup
$this->app->bind(fugaRepositoryInterface::class, function () use ($id)) {
$mock = Mockery::mock(fugaRepository::class);
$mock->shouldReceive('何かしら')
->once()
->with($id)
->andThrow(AccessDeniedHttpException::class, $message, $code);
return $mock;
});
// exercise
$hogeService = resolve(hogeServiceInterface::class);
// verify
$actual = $hogeService->hoge($id);
$this->assertTrue($actual);
}
}
Type error: Argument 2 passed to Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException::__construct() must be an instance of Exception or null, int given, called in mockery/mockery/library/Mockery/Expectation.php on line 607
原因を探るべく、andTrowのコードを見てみました。
下記のように第一引数によって処理を分け、 andReturn
に渡していることがわかります。
先程の例の場合は、 else
文を通るので、 $message, $code, $previous
の順で渡してインスタンスを生成します。
public function andThrow($exception, $message = '', $code = 0, \Exception $previous = null)
{
$this->_throw = true;
if (is_object($exception)) {
$this->andReturn($exception);
} else {
$this->andReturn(new $exception($message, $code, $previous));
}
return $this;
}
次に AccessDeniedHttpException
のコードをみてみます。
コンストラクタで $message, $previous, $code
の順で受け取っており、 andThrow
で渡している引数と順番が違うことがわかります。
そのため、エラーになったというわけですね。
class AccessDeniedHttpException extends HttpException
{
/**
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(403, $message, $previous, [], $code);
}
}
こういったケースで andThrow
を使うなら、例外インスタンスを生成して渡した方が良さそうですね。
andThrow(new AccessDeniedHttpException($message, null, $code));
まとめ
MockeryのandThrowは第一引数がインスタンスか例外クラスで切り分けしており、第一引数が例外クラスのときは、例外クラスがメッセージ、コードの順じゃないとエラーになるということがわかりました。
etc AccessDeniedHttpException
また andThrow
は内部で andReturn
を呼んでいることも知れてよかったです。