ヘルパ関数を使う
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点。
- URL::shouldReceive('route')は何をモックしたか
- デフォルト引数が存在している。
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())
簡単だから誰か直して(((