Laravel 5.6 で \Illuminate\Support\Facades\Hash::shouldReceive('make') が使えなくなったので tests 用の代わりの方法を用意した

訳あって laravel 5.6 に upgrade したら、 tests/* で使ってた `\Illuminate\Support\Facades\Hash::shouldReceive('make')->andReturn('dummy-password');` が使えなくなったので、取り敢えずの代替方法を用意した。

5.5.* から 5.6.* に Upgrade した際の簡易メモ。

- composer.json を 5.6 の状態に合わせる
- `./config/hashing.php` と `./config/logging.php` を 5.6 から取得してくる
- `./config/app.php` から `Logging Configuration` セクションを削除する
- `./config/broadcasting.php` を 5.6.x の内容に合わせる(`connections -> pusher -> options` の内容を 5.6.x の通りに追加)
- `./config/filesystems.php` を 5.6.x の内容に合わせる(`disks -> s3 -> 'url' => env('AWS_URL'),` を追加)
- `./config/queue.php` を 5.6.x の内容に合わせる(`connections -> sqs -> key, secret, prefix, queue, region` と `connections -> redis ->block_for` を修正)

これらを修正した上で、 phpunit を走らせると、 `Hash::shouldReceive('make')` で尽くコケた。

まぁ、当然なんやけど。

## 結論

以下のような helper method を TestCase.php に追加した。

```tests/TestCase.php
    protected function freezeBcrypt($fixedPassword)
    {
        $bcryptHasher = Mockery::mock(\Illuminate\Hashing\BcryptHasher::class);
        $bcryptHasher->shouldReceive('make')->andReturn($fixedPassword);
        $hashManager = Mockery::mock(\Illuminate\Hashing\HashManager::class);
        $hashManager->shouldReceive('driver')->withArgs(['bcrypt'])->andReturn($bcryptHasher);
        app()->bind('hash', function () use ($hashManager) {
            return $hashManager;
        });
    }
```

`times()` などは検査してないので、もし、それらが必要ならば適切に修正するべきだと思う。

使い方は言うまでもなく、各 Test File で `$this->freezeBcrypt('dummy-passowrd');` とかして使う。

## どのように対応したか

### まずは 5.6.x の bcrypt() helper の中身を見る

bcrypt() helper の中身は、 `/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php` に書いてある。

※ 任意の場所(適当な Controller とか web.php とかお好きな場所)に `bcrypt();` と書き、 PhpStorm などから、  記載した `bcrypt()` helper を `cmd + (click)` すると、実装コードにジャンプする。

```/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
if (! function_exists('bcrypt')) {
    /**
     * Hash the given value against the bcrypt algorithm.
     *
     * @param  string  $value
     * @param  array  $options
     * @return string
     */
    function bcrypt($value, $options = [])
    {
        return app('hash')->driver('bcrypt')->make($value, $options);
    }
}
```

※ 現時点の 5.6.3 では、上記（ https://github.com/laravel/framework/blob/v5.6.3/src/Illuminate/Foundation/helpers.php#L186-L198 ）となっていた。

### 5.5.x から何が変わったのか

5.5.x の頃のコードを確認してみる( https://github.com/laravel/framework/blob/v5.5.34/src/Illuminate/Foundation/helpers.php#L186-L198 )と、以下のようになっていた。

```/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
if (! function_exists('bcrypt')) {
    /**
     * Hash the given value.
     *
     * @param  string  $value
     * @param  array   $options
     * @return string
     */
    function bcrypt($value, $options = [])
    {
        return app('hash')->make($value, $options);
    }
}
```

https://laravel.com/docs/5.6/upgrade#upgrade-5.6.0 の H3 tag `Hashing` の箇所を見ても分かる通り、 `config/hashing.php` の設定で `bcrypt` と `argon ` をサポートしたよってことらしい。

それによって、 `driver` を選択するステップが増えてる。

※ ちなみに 5.6 の https://laravel.com/docs/5.6/hashing を見る限り、 `argon` は `requires PHP 7.2.0 or greater.` だそうです。

取り敢えず、今は bcrypt のままで進めるとして、 `./config/hashing.php` は `driver -> bcrypt` としておく。(5.6.x から取得したデフォルトの状態のまま。)

### driver 選択のステップを吸収する

取り敢えずは、素直に、 `app('hash')` を差し替えるアプローチとした。

- `app('hash')` を `$hashManager` に差し替える
- `$hashManager` の `driver` メソッドを呼ぶと、 `$bcryptHasher` を返却するようにしておく
- `$bcryptHasher` は `make` メソッドを呼ぶと、 `$fixedPassword` を返却する。

という実装が前述と同じ以下のコード。

```tests/TestCase.php
    protected function freezeBcrypt($fixedPassword)
    {
        $bcryptHasher = Mockery::mock(\Illuminate\Hashing\BcryptHasher::class);
        $bcryptHasher->shouldReceive('make')->andReturn($fixedPassword);
        $hashManager = Mockery::mock(\Illuminate\Hashing\HashManager::class);
        $hashManager->shouldReceive('driver')->withArgs(['bcrypt'])->andReturn($bcryptHasher);
        app()->bind('hash', function () use ($hashManager) {
            return $hashManager;
        });
    }
```

どこまで担保するべきか、という問題はあるけど、取り敢えず、今回は times() の検証は行ってない。

## 最後に

事前に `Hash::shouldReceive('make')->andReturn('dummy-password');` 自体をちゃんと TestCase の helper Method にしておけば良かった。

散見された為、修正して回る羽目になった。

いつか driver `argon` も `times()` も対応できるようにしたいね。

あと、 `app()` を経由して差し替えるアプローチはあまり望ましくないかもしれないので、`容量用法をお守り下さい`という気持ちを忘れずにするのが大切かも知れない。

というメモ。