はじめに
Laravelでテストを実装中に「Class "Database\Factories\MessageRoomFactory" not found」というエラーに遭遇しました。
今回は、database/factoriesに新しくMessageRoomFactoryクラスを作成することで対処ができました。」
環境
Laravel 9.52.16
問題
- 次のようなテストを実行したい
MessagesControllerTest.php(該当メソッドのみ)
public function test_Messages_store()
{
// テストユーザを作成
$user = User::factory()->create();
// メッセージルームを作成
$message_room = MessageRoom::factory()->create();
// テスト用のフォームデータを作成
$formData = [
'message' => 'テストメッセージ',
];
// 作成したユーザでアクセス
$response = $this->actingAs($user)->post('/my/messages/store/' . $message_room->id, $formData);
$response->assertStatus(302);
$this->assertDatabaseHas('messages', [
'message' => $formData['message'],
'message_room_id' => $message_room->id,
'user_id' => $user->id,
]);
}
- しかし実行すると次のようなエラーが出る
root@:/var/www/html/niches# php artisan test --filter MessagesControllerTest
Deprecated: PHP Startup: Use of mbstring.internal_encoding is deprecated in Unknown on line 0
Deprecated: PHP Startup: Use of mbstring.internal_encoding is deprecated in Unknown on line 0
FAIL Tests\Feature\Http\Controllers\MessagesControllerTest
⨯ messages store
---
• Tests\Feature\Http\Controllers\MessagesControllerTest > messages store
PHPUnit\Framework\ExceptionWrapper
Class "Database\Factories\MessageRoomFactory" not found
at vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:821
817▕ public static function factoryForModel(string $modelName)
818▕ {
819▕ $factory = static::resolveFactoryName($modelName);
820▕
➜ 821▕ return $factory::new();
822▕ }
823▕
824▕ /**
825▕ * Specify the callback that should be invoked to guess factory names based on dynamic relationship names.
Tests: 1 failed
Time: 15.79s
「クラス"Database\Factories\MessageRoomFactory"が見つかりません」とのこと。
解決法
- database/factoriesを確認してみると、確かにMesssageRoomFactoryがなかった
- これにより、テストレコードを作成するためのfactory()メソッドが使えなかったっぽい
- database/factoriesにMessageRoomFactoryクラスを追加する
database/factories/MessageRoomFactory.php
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\MessageRoom>
*/
class MessageRoomFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
];
}
}
root@:/var/www/html/niches# php artisan test --filter MessagesControllerTest
Deprecated: PHP Startup: Use of mbstring.internal_encoding is deprecated in Unknown on line 0
Deprecated: PHP Startup: Use of mbstring.internal_encoding is deprecated in Unknown on line 0
PASS Tests\Feature\Http\Controllers\MessagesControllerTest
✓ messages store
Tests: 1 passed
Time: 12.13s
これで無事解決です!
補足
通常、Factoryにはテスト用のデフォルト値を設定しなければいけません。
しかし今回はテーブルが'id'、'timestamps'のみであり、Eloquentが勝手に補完してくれるので、空でも問題ありません。
mysql> desc message_rooms;
+------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------------+------+-----+---------+----------------+
| id | bigint unsigned | NO | PRI | NULL | auto_increment |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+------------+-----------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
まず、主キーである'id'カラムは自動インクリメントにより値を指定する必要がありません。
また、'created_at'と'updated_at'はEloquentモデルが自動的に埋めてくれます。
よって今回のケースでは、フィールドを定義する必要がありません。
database/factories/MessageRoomFactory.php
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\MessageRoom>
*/
class MessageRoomFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
// 今回はフィールドを定義する必要はなし
return [
];
}
}
おわりに
なんとなくFactoryクラスはモデルとセットで生成されると勘違いしていましたが、どうやら違うようでした。
やはりテストをする時にはFactoryクラスを使うのが便利ですね!
参考