PHP Mockey 特定のオブジェクトが引数として渡されたときの戻り値を切り替える方法


動作環境

PHP 7.2
PHPUnit 6.5.14
Mockery 1.2.2

やりたいこと

Mockeryでモック化したUserRepositoryクラスのメソッドisExistの動作を引数によって切り替えたい

isExistメソッドの役割:指定されたユーザIDが存在している場合 true 存在していない場合 false を返す

コード


interface UserRepository
{
    /**
     * @param UserId $userId
     * @return bool true:ユーザが存在する false:ユーザが存在しない
     */
    public function isExist(UserId $userId): bool;
}

class UserId
{
    /**
     * @var int
     */
    protected $id;
    /**
     * UserId constructor.
     * @param int $id
     */
    public function __construct(int $id)
    {
        $this->id = $id;
    }

    /**
     * @return int
     */
    public function getId(): int
    {
        return $this->id;
    }
}

//~~~~~~~~~ 以下テストケース内

$mockUserRepository = \Mockery::mock(UserRepository::class);

$mockUserRepository->shouldReceive('isExist')
            ->byDefault()
            ->andReturn(true);

$mockUserRepository->shouldReceive('isExist')
            ->with(\Mockery::on(function (UserId $userId) {
                if ($userId->getId() === 999) { //ユーザIDが 999 の場合に andReturn に指定された値を返却する
                    return true;
                }
                return false;
            }))
            ->andReturn(false);


$this->assertTrue($this->mockUserRepository->isExist(new UserId(1))); // OK
$this->assertFalse($this->mockUserRepository->isExist(new UserId(999))); // OK

ポイント

Mockeryのwithメソッドは通常値を設定するだけで、動作を切り替えたりできますが、
今回のように「オブジェクトが引数」となる場合はだたwithを使うだけだと想定どおりに動作してくれません。

誤りのケース
$mockUserRepository->shouldReceive('isExist')
            ->byDefault()
            ->andReturn(true);
$mockUserRepository->shouldReceive('isExist')
            ->with(new UserId(999)) // new UserId(999) === new UserId(999) → false
            ->andReturn(false);

$this->assertTrue($this->mockUserRepository->isExist(new UserId(1))); // OK
$this->assertFalse($this->mockUserRepository->isExist(new UserId(999))); // NG defaultのほうが呼ばれてしまっている