3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

MockeryのandThrowの引数について学んだ

Posted at

概要

前回の記事で紹介しましたが、テストコードでモックを使う際は、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 を呼んでいることも知れてよかったです。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?