エンジニアのひよこ_level10

毎日更新してた人。たまに記事書きます。

【Laravel】ヘルパ関数を使った時のshouldRecieveのwithの落とし穴【762日目】

ヘルパ関数を使う

route('user.mypage', ['id' => 1]);

これのテストコードを書きたい。

動かないコード

こんな感じの書いた時、routeはURLファサードで動くので、こうなる気がする

URL::shouldReceive('route')
            ->with('user.mypage', ['id' => 1])
            ->once()
            ->andReturn('https://example.com');

問題なさそうに見えますが、動かない。

Mockery\Exception\NoMatchingExpectationException: No matching handler
Either the method was unexpected or its arguments matched no expected argument list for this method

なぜか。

呼び出す流れを見よう

まず、

route('user.mypage', ['id' => 1]);

このコードが呼び出すコードですが

vendor/laravel/framework/src/Illuminate/Foundation/helpers.php

if (! function_exists('route')) {
    function route($name, $parameters = [], $absolute = true)
    {
        return app('url')->route($name, $parameters, $absolute);
    }
}

このコードが呼ばれます。ここで注目すべきは2点。

  1. URL::shouldReceive('route')は何をモックしたか
  2. デフォルト引数が存在している。

1. URL::shouldReceive('route')は何をモックしたか

function route($name, $parameters = [], $absolute = true)
{
    return app('url')->route($name, $parameters, $absolute); //ここをモックしている
}

この部分を置き換えるのが、先程のコード。

そして問題となるのは、その少し上のデフォルト引数。

2. デフォルト引数が存在している。

function route($name, $parameters = [], $absolute = true) //ここにデフォルト引数がある
{
    return app('url')->route($name, $parameters, $absolute); 
}

デフォルト引数があるのを考慮して、先程のroute()はどの様に動くか。

function route('user.mypage', ['id' => 1], true)
{
    return app('url')->route('user.mypage', ['id' => 1], true); // この時点で、引数が3つになっている
}

このように、モックした関数が動くときには、引数が3つに増えています。
そうなると、withで書いたのは2つの引数のはずが、実際には3つの引数が使われているってことで、エラーを吐くわけですね。

では、デフォルト引数で追加される値を考慮して、動くコードを書いてみましょう。

動くコード

URL::shouldReceive('route')
            ->with('user.mypage', ['id' => 1], true) //第3引数にtrueを追加
            ->once()
            ->andReturn('https://example.com');

これで動きます。

(おまけ)本当にそうか試す方法

Laravelのコードをいじることが出来るなら、元コードをいじるとわかりやすいかもしれません。

function route($name, $parameters = [], $absolute = true)
{
    return app('url')->route($name, $parameters); //ここの第3引数を消す
}

こうすると、最初に書いたテストコードでも動きます。

ぼやき

これ系の罠、実は今でも治ってなくて、大量にあります/(^o^)\

私がLaravelコントリビュートしたっていうのも、これ系の一部です。(cache())

簡単だから誰か直して(((