環境
- Laravel 5.6
-
composer install
した直後の状態
tearDown()
Laravelでテストコードを書く際、テスト後の後処理をtearDown()に書くと思います。
例えば、テスト用に設定したキャッシュを削除するため、Cacheファサードのforget()
メソッドを呼び出したいとします。
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()は何をやっているのでしょうか。
呼び出されるのは下記のコードです。
/**
* 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();
}
ここで注目するのは、 $this->app->flush();
です。
$this->flush()
で呼び出される処理は下記のコードです。
$this->flush()
/**
* 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 = [];
}
/**
* Flush the container of all bindings and resolved instances.
*
* @return void
*/
public function flush()
{
$this->aliases = [];
$this->resolved = [];
$this->bindings = [];
$this->instances = [];
$this->abstractAliases = [];
}
上記のコードを読んでもらえば察しが付くかと思いますが、$this->flush()
はAliasやFacadeの登録情報をすべて空配列に置き換えています。
つまり、起動時に登録された情報が無くなってしまいます。
そのため、parent::tearDown()
を読んだ後ではCacheファサードが使えなくなっていたんですね。
どう対応すべきか
parent::tearDown()
後にファサードが動かない場合、どうすべきか。
私が思いつくのは下記の通りです。
- 各Example最後に削除処理を書く
- parent::tearDown()前に削除処理を書く
- 外部にデータを置かずモックを使ってテスト内で完結させる
- 頑張ってもう一度登録情報を設定し直す
一番手頃なのは2番ですが、テストであるという事を考えると3番でやっていきたいところ。
ただし、ファサードのモックはFeatureテストだと上手くいかないケースもあり…。
特にテストの在り方にこだわりが無ければ2番で実装するのが良いかと思います。