イントロダクション
Laravelでは、データベースを駆動するアプリケーションのテストを簡単にできる、便利で様々なツールを用意しています。その一つは、指定した抽出条件に一致するデータがデータベース中に存在するかをアサートする、seeInDatabase
ヘルパです。たとえば、user
テーブルの中にemail
フィールドがsally@example.com
の値のレコードが存在するかを確認したいとしましょう。次のようにテストできます。
public function testDatabase()
{
// アプリケーションを呼び出す…
$this->seeInDatabase('users', [
'email' => 'sally@example.com'
]);
}
もちろん、seeInDatabase
メソッドやその他のヘルパは、利便性のために用意しています。PHPUnitの組み込みアサートメソッドは、テストで自由に使用できます。
各テスト後のデータベースリセット
前のテストデータが、それに引き続くテストへ影響をあたえないように、各テストが終了するごとにデータベースをリセットするのは、時に便利です。
マイグレーションの使用
一つのアプローチは、それぞれのテストの後にデータベースをロールバックし、次のテストの前にマイグレーションする方法です。Laravelはこれを自動的に処理するために、シンプルなDatabaseMigrations
トレイトを用意しています。テストクラスでこのトレイトを使用するだけで、すべて処理されます。
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
use DatabaseMigrations;
/**
* 基本的な機能テストの例
*
* @return void
*/
public function testBasicExample()
{
$this->visit('/')
->see('Laravel 5');
}
}
トランザクションの使用
データベースをリセットするもう一つのアプローチは、各テストケースをデータベーストランザクションでラップしてしまうことです。Laravelはこれを自動的に処理する、便利なDatabaseTransactions
トレイトを用意してます。
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
use DatabaseTransactions;
/**
* 基本的な機能テストの例
*
* @return void
*/
public function testBasicExample()
{
$this->visit('/')
->see('Laravel 5');
}
}
Note: デフォルトでは、このトレイトは、トランザクション中のデフォルトデータベース接続をラップしているだけです。もし、アプリケーションで複数のデータベース接続を使用している場合は、テストクラスの
$connectionsToTransact
プロパティを定義する必要があります。このプロパティはトランザクションで実行する接続名の配列を指定します。
ファクトリの記述
テスト時、何件かのレコードをデータベースに挿入する必要が起きることがあります。こうしたテストデータを作る時に、手動でそれぞれのカラムへ値を指定する代わりに、Laravelでは「ファクトリ」を使用しEloquentモデルの各属性にデフォルトを設定できます。手始めに、アプリケーションのdatabase/factories/ModelFactory.php
ファイルを見てください。このファイルには最初からファクトリの定義が含まれています。
$factory->define(App\User::class, function (Faker\Generator $faker) {
static $password;
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => $password ?: $password = bcrypt('secret'),
'remember_token' => str_random(10),
];
});
ファクトリで定義しているクロージャの中から、モデルの全属性に対するデフォルトのテスト値を返しています。このクロージャはFaker PHPライブラリーのインスタンスを受け取っており、これはテストのための多用なランダムデータを生成するのに便利です。
もちろん追加のファクトリをModelFactory.php
に自由に追加できます。系統立てるためには各モデルのファクトリーファイルを追加したほうが良いでしょう。たとえば、database/factories
ディレクトリの中へUserFactory.php
とCommentFactory.php
ファイルを作成します。
factories
ディレクトリ中のファイルは、自動的にLaravelにより読みこまれます。
ファクトリステート
ステートにより、どんな組み合わせのモデルファクトリに対しても、変更を取り消す定義を行うことができます。たとえば、User
モデルがデフォルトの属性値を変更する、delinquent
ステートを持っているとしましょう。state
メソッドを使い、状態の変更を定義できます。
$factory->state(App\User::class, 'delinquent', function ($faker) {
return [
'account_status' => 'delinquent',
];
});
ファクトリの使用
モデルの生成
ファクトリを定義し終えたら、テストかデータベースのシーディング(初期値設定)ファイルの中で、グローバルなfactory
関数を使用してモデルインスタンスを生成できます。では、モデル生成の例をいくつか見てみましょう。最初はmake
メソッドでモデルを生成し、データベースには保存しないでみましょう。
public function testDatabase()
{
$user = factory(App\User::class)->make();
// モデルをテストで使用…
}
さらにモデルのコレクションや指定したタイプのモデルも生成できます。
// App\Userインスタンスを3つ生成
$users = factory(App\User::class, 'admin', 3)->make();
ステートの適用
$users = factory(App\User::class, 5)->states('delinquent')->make();
$users = factory(App\User::class, 5)->states('premium', 'delinquent')->make();
$users = factory(App\User::class, 5)->states('premium', 'deliquent')->make();
属性のオーバーライド
モデルのデフォルト値をオーバーライドしたい場合は、make
メソッドに配列で値を渡してください。指定した値のみ置き換わり、残りの値はファクトリで指定したデフォルト値のまま残ります。
$user = factory(App\User::class)->make([
'name' => 'Abigail',
]);
モデルの保存
create
メソッドはモデルインスタンスを生成するだけでなく、Eloquentのsave
メソッドを使用しデータベースへ保存します。
public function testDatabase()
{
// 一つのApp\Userインスタンスを作成
$user = factory(App\User::class)->create();
// 3つのApp\Userインスタンスを作成
$users = factory(App\User::class, 3)->create();
// モデルをテストで使用する…
}
create
メソッドに配列で値を渡すことで、モデルの属性をオーバーライドできます。
$user = factory(App\User::class)->create([
'name' => 'Abigail',
]);
リレーション
以下の例では、生成したモデルにリレーションを付けています。複数モデルの生成にcreate
メソッドを使用する場合、インスタンスのコレクションが返されます。そのため、コレクションで使用できるeach
などの便利な関数が利用できます。
$users = factory(App\User::class, 3)
->create()
->each(function ($u) {
$u->posts()->save(factory(App\Post::class)->make());
});
リレーションと属性クロージャ
クロージャ属性をファクトリ定義の中で使い、モデルとのリレーションを追加することもできます。たとえば、Post
を作成する時に、新しいUser
インスタンスも作成したい場合は、以下のようになります。
$factory->define(App\Post::class, function ($faker) {
return [
'title' => $faker->title,
'content' => $faker->paragraph,
'user_id' => function () {
return factory(App\User::class)->create()->id;
}
];
});
さらに、クロージャは評価済みのファクトリーの配列を受け取ることもできます。
$factory->define(App\Post::class, function ($faker) {
return [
'title' => $faker->title,
'content' => $faker->paragraph,
'user_id' => function () {
return factory(App\User::class)->create()->id;
},
'user_type' => function (array $post) {
return App\User::find($post['user_id'])->type;
}
];
});