parent::tearDown()後にAliasやFacadeが使えなくなる原因を追う

# 環境

* Laravel 5.6
* `composer install` した直後の状態

# tearDown()

Laravelでテストコードを書く際、テスト後の後処理をtearDown()に書くと思います。
例えば、テスト用に設定したキャッシュを削除するため、Cacheファサードの`forget()`メソッドを呼び出したいとします。

``` php
namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Support\Facades\Cache;

class ExampleTest extends TestCase
{
    protected function tearDown()
    {
        parent::tearDown(); // TODO: Change the autogenerated stub

        Cache::forget('hoge'); // テスト用に設置したキャッシュを削除したい
    }
}


```

しかし、上記コードは動きません。
Cacheクラスは登録されていない、という類のエラーが出ます。

どうしてCacheが動かなくなるのかを追っていきます。

# parent::tearDown()

parent::tearDown()は何をやっているのでしょうか。
呼び出されるのは下記のコードです。

``` php
    /**
     * Clean up the testing environment before the next test.
     *
     * @return void
     */
    protected function tearDown()
    {
        if ($this->app) {
            foreach ($this->beforeApplicationDestroyedCallbacks as $callback) {
                call_user_func($callback);
            }

            $this->app->flush();

            $this->app = null;
        }

        $this->setUpHasRun = false;

        if (property_exists($this, 'serverVariables')) {
            $this->serverVariables = [];
        }

        if (property_exists($this, 'defaultHeaders')) {
            $this->defaultHeaders = [];
        }

        if (class_exists('Mockery')) {
            if ($container = Mockery::getContainer()) {
                $this->addToAssertionCount($container->mockery_getExpectationCount());
            }

            Mockery::close();
        }

        if (class_exists(Carbon::class)) {
            Carbon::setTestNow();
        }

        $this->afterApplicationCreatedCallbacks = [];
        $this->beforeApplicationDestroyedCallbacks = [];

        Artisan::forgetBootstrappers();
    }
```
https://github.com/laravel/framework/blob/5.7/src/Illuminate/Foundation/Testing/TestCase.php#L130-L173

ここで注目するのは、` $this->app->flush();` です。
`$this->flush()`で呼び出される処理は下記のコードです。

# $this->flush()

``` php
    /**
     * Flush the container of all bindings and resolved instances.
     *
     * @return void
     */
    public function flush()
    {
        parent::flush();

        $this->buildStack = [];
        $this->loadedProviders = [];
        $this->bootedCallbacks = [];
        $this->bootingCallbacks = [];
        $this->deferredServices = [];
        $this->reboundCallbacks = [];
        $this->serviceProviders = [];
        $this->resolvingCallbacks = [];
        $this->afterResolvingCallbacks = [];
        $this->globalResolvingCallbacks = [];
    }
```
https://github.com/laravel/framework/blob/5.7/src/Illuminate/Foundation/Application.php#L1121-L1140

``` php
    /**
     * Flush the container of all bindings and resolved instances.
     *
     * @return void
     */
    public function flush()
    {
        $this->aliases = [];
        $this->resolved = [];
        $this->bindings = [];
        $this->instances = [];
        $this->abstractAliases = [];
    }
```
https://github.com/laravel/framework/blob/5.7/src/Illuminate/Container/Container.php#L1164-L1176

上記のコードを読んでもらえば察しが付くかと思いますが、`$this->flush()`はAliasやFacadeの登録情報をすべて空配列に置き換えています。
つまり、起動時に登録された情報が無くなってしまいます。

そのため、`parent::tearDown()` を読んだ後ではCacheファサードが使えなくなっていたんですね。

# どう対応すべきか

`parent::tearDown()`後にファサードが動かない場合、どうすべきか。
私が思いつくのは下記の通りです。

1. 各Example最後に削除処理を書く
2. parent::tearDown()前に削除処理を書く
3. 外部にデータを置かずモックを使ってテスト内で完結させる
4. 頑張ってもう一度登録情報を設定し直す

一番手頃なのは2番ですが、テストであるという事を考えると3番でやっていきたいところ。
ただし、ファサードのモックはFeatureテストだと上手くいかないケースもあり…。
特にテストの在り方にこだわりが無ければ2番で実装するのが良いかと思います。