他のテストのデータが干渉して、テストが通らないときの解決策
Laravelのテストで use RefreshDatabase
をすると毎回テスト用データベースをロールバックしてくれるため、テストごとにテストデータを入れて検証することができる。
しかし、 use RefreshDatabase
をしてもうまくテストデータがリセットされてないっぽい現象がおきてしまった。
現象を整理する
このようなテストファイルがあるとする。
なるべくかんたんな例にするために、ユーザーを作成するAPIのテストをイメージして作った。
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class UserCreateTest extends TestCase
{
use WithFaker;
/**
* ユーザー作成テスト
*/
public function testCreateUser()
{
$admin = Admin::factory()->create();
$this->actingAs($admin);
$param = ['name' => $this->faker->name()];
$response = $this->post('/api/user', $param);
$response->assertStatus(201);
$this->assertDatabaseHas('users', [
'name' => $param['name']
]);
$this->assertDatabaseCount('users', 1);
}
}
このファイルを実行すると、普通なら成功するのだが、僕の場合はassertDatabaseCountもところで「テーブルに2個レコードがあるよー」とエラーになった。
原因はおそらく他のテストファイルで use RefreshDatabase
を記述していないファイルがあったことなのだが、諸事情でその原因元のファイルに use RefreshDatabse
を記述することができない。
解決策は、** 「テストファイル内で phpartisan migrate:fresh
を実行すること!」** です。
やや強引ですが、解決しちゃうのでやってみます。
テストファイル内で migrate:freshを実行する
Laravelのテストで使われているPHPUnitでは、 setUpメソッドとtearDownメソッドがある。
setUp
メソッドではテストファイル内の各テストが行われる前に実行されるメソッドで、 tearDown
は各テストが行われた後に実行されるメソッドである。
僕の場合は、 setUpメソッドでこのようにマイグレーションを実行したらうまくいきました。
protected function setUp(): void
{
parent::setUp();
$this->artisan('migrate:fresh');
}
各テストの前に実行されているのでやや冗長かもしれませんが、一旦の回避策としてこれでいけました。
僕はまだ試していないですが、 setUpBeforeClass
や tearDownAfterClass
というメソッドでテストごとではなく、テストが記述されているクラスの前後で処理を実行できるようなのでこちらでも良いかもしれません。
テストデータで変だな〜って思ったら $this->artisan('migrate:fresh');
を試してみてください!
Discussion