【Laravel】PHPUnit:テストデータ生成とDATE型カラムのエラー対処
はじめに
PHPUnitのテストコードについて新しく知見が増えたため、
備忘録として残します。
factoryでテストデータを生成する
factoryを使用することで、テスト時にマスターデータを作成するなど
テストデータを生成することができます。
テストデータ生成
php artisan make:factory CategoryFactory
コマンドで作成したfactoryに
以下のようにカラム名と値を定義します。(カテゴリーの例)
class CategoryFactory extends Factory
{
// カテゴリーモデルが対象
protected $model = Category::class;
public function definition(): array
{
return [
'name' => $this->faker->word,
];
}
}
factoryを呼び出し
そして、テストコードでは以下のようにfactoryを呼び出すのみです。
テスト時にカテゴリーのレコードを登録し、それを後の処理で使用でき、
また、count()
により複数のレコードを登録することもできます。
public function create_task_success()
{
// カテゴリーのレコードを一つ登録
$category = Category::factory()->create();
// 複数のレコードを登録
$categories = Category::factory()->count(3)->create();
// 処理
// ...
$response = $this->postJson('/api/tasks', $data);
}
unique
unique()
は、Factoryで生成されるデータが重複しないようにします。
以下の例では```unique()``を使用し、ユニークなnameを生成しています。
public function definition(): array
{
return [
'name' => $this->faker->unique()->word,
];
}
このメソッドは、DBのユニーク制約が設定されているカラムに対して役に立ちます。
例えば、例のfactory()->count(3)->create()
を用いて
一度に複数のレコードを作成する際に、ユニーク制約違反によるエラーを防ぐことができます。
もしunique()
を使用しない場合、重複による失敗は毎回ではなく時々起きるため、
気づくのが遅れる可能性があります。
※例えばdayOfWeek()
のように、生成する曜日の選択肢が限られている場合、重複エラーが発生しやすくなります。
そのため、ユニーク制約のカラムではunique()
を使用することが望ましいです。
DATE型とキャスト:エラーの原因と対策
タスクの登録テスト時にassertDatabaseHas()
を使い、以下のようにデータが登録されているかを確認していました。
$data = [
'category_id' => 3,
'title' => 'タスクテスト',
'start_date' => '2023-09-28',
];
// POST送信
$response = $this->postJson('/api/tasks', $data);
// データベースに正しいデータが保存されているか検証
$this->assertDatabaseHas('tasks', [
'category_id' => $data['category_id'],
'title' => $data['title'],
'start_date' => $data['start_date'],
]);
POST先では特に値の変換はしていないため、成功するかと思っていましたが、
assertDatabaseHas
で「start_date」カラムにエラーが発生しました。
原因
start_dateはDATE型のカラムであり、以下のように
キャストでdate属性に型変換されるデータのためエラーが発生しました。
class Task extends Model
{
protected $casts = [
'start_date' => 'date',
];
}
キャストにより、DBから「start_date」のデータを取得時にCarbonインスタンスとして取得されるため、文字列としてフォーマットすると、時間部分が「00:00:00」として表示されます。
※DBでは、時間部分はなく日付のみが保存されている
そのため、start_dateの値は「2023-09-28」でPOST送信しているにも関わらず、
assertDatabaseHas
の検証時に、「2023-09-28 00:00:00」となることによりエラーが発生していました。
解決
これは、「start_date」のみassertDatabaseHas
から外し、
新たに取得した「start_date」を「Y-m-d」の形式に変換し、比較することで解決できます。
// 処理 ...
$response = $this->postJson('/api/tasks', $data);
// データベースに正しいデータが保存されているか検証
$this->assertDatabaseHas('tasks', [
'category_id' => $data['category_id'],
'title' => $data['title'],
]);
// toDateString()で「Y-m-d」の形式にする
$task = Task::first();
$this->assertEquals($data['start_date'], $task->start_date->toDateString());
日付や時間の検証は少し複雑なため、他のデータとは別に検証することも考えてもいいかと思いました。
補足
以下のエラーは、テストファイルを「Unit」で作成し、テストを行った際に発生しました。
A facade root has not been set.
Unitを作成する際のデフォルトでは、use PHPUnit\Framework\TestCase
となっていますが、これはPHPUnitのクラスです。
そのため、ファザードやヘルパー関数など、Laravel特有の機能を使用できず、エラーが発生します。
この問題は、use Tests\TestCase
に変更することで解決でき、これにより、Laravel特有の機能が使用できるようになります。
Discussion