Laravel 7.x データベースのテスト

イントロダクション

Laravelでは、データベースを駆動するアプリケーションのテストを簡単にできる、便利で数多くのツールを用意しています。その一つは、指定した抽出条件に一致するデータがデータベース中に存在するかをアサートする、assertDatabaseHasヘルパです。たとえば、usersテーブルの中にemailフィールドがsally@example.comの値のレコードが存在するかを確認したいとしましょう。次のようにテストできます。

public function testDatabase()
{
    // アプリケーションを呼び出す…

    $this->assertDatabaseHas('users', [
        'email' => 'sally@example.com',
    ]);
}

データベースにデータが存在しないことをアサートする、assertDatabaseMissingヘルパを使うこともできます。

assertDatabaseHasメソッドやその他のヘルパは、皆さんが便利に使ってもらうため用意しています。PHPUnitの組み込みアサートメソッドは、機能テストで自由に使用できます。

ファクトリの生成

ファクトリを生成するには、make:factory Artisanコマンドを使用します。

php artisan make:factory PostFactory

新しいファクトリは、database/factoriesディレクトリに設置されます。

--modelオプションにより、ファクトリが生成するモデルの名前を指定できます。このオプションは、生成するファクトリファイルへ指定モデル名を事前に設定します。

php artisan make:factory PostFactory --model=Post

各テスト後のデータベースリセット

前のテストがその後のテストデータに影響しないように、各テストの後にデータベースをリセットできると便利です。インメモリデータベースを使っていても、トラディショナルなデータベースを使用していても、RefreshDatabaseトレイトにより、マイグレーションに最適なアプローチが取れます。テストクラスにてこのトレイトを使えば、すべてが処理されます。

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * 基本的な機能テストの例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $response = $this->get('/');

        // …
    }
}

ファクトリの記述

テスト実行前に、何件かのレコードをデータベースに挿入する必要があります。こうしたテストデータを作る時に、手動でそれぞれのカラムへ値を指定する代わりに、Laravelではモデルファクトリを使用し、Eloquentモデルの各属性にデフォルト値を設定できます。手始めに、アプリケーションのdatabase/factories/UserFactory.phpファイルを見てください。このファイルには初めからファクトリの定義が一つ含まれています。

use Faker\Generator as Faker;
use Illuminate\Support\Str;

$factory->define(App\User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
        'remember_token' => Str::random(10),
    ];
});

ファクトリで定義しているクロージャの中から、モデルの全属性に対するデフォルトのテスト値を返しています。このクロージャはFaker PHPライブラリーのインスタンスを受け取っており、これはテストのための多用なランダムデータを生成するのに便利です。

より組織立てるために、各モデルごとに追加のファクトリファイルを作成することもできます。たとえば、database/factoriesディレクトリにUserFactory.phpCommentFactory.phpファイルを作成できます。factoriesディレクトリ下の全ファイルは、Laravelにより自動的にロードされます。

Tip!! Fakerのローケルは、config/app.php設定ファイルのfaker_localeオプションで指定できます。

ファクトリの拡張

モデルを拡張する場合、テストやシーディングで子モデルのファクトリ属性を活用するため、ファクトリも同様に拡張すると便利です。これを実現するため、ファクトリビルダのrawメソッドを呼び出し、指定したファクトリの属性配列をそのまま取得できます。

$factory->define(App\Admin::class, function (Faker\Generator $faker) {
    return factory(App\User::class)->raw([
        // …
    ]);
});

ファクトリステート

ステートにより、モデルファクトリのどんな組み合わせに対しても適用できる、個別の調整を定義できます。たとえば、Userモデルは、デフォルト属性値の一つを変更する、delinquentステートを持つとしましょう。stateメソッドを使い、状態変換を定義します。単純なステートでは、属性の変換の配列を渡します。

$factory->state(App\User::class, 'delinquent', [
    'account_status' => 'delinquent',
]);

ステートで計算や$fakerインスタンスが必要な場合は、ステートの属性変換を計算するために、クロージャを使います。

$factory->state(App\User::class, 'address', function ($faker) {
    return [
        'address' => $faker->address,
    ];
});

ファクトリコールバック

ファクトリコールバックはafterMakingafterCreatingメソッドを使用し登録し、モデルを作成、もしくは生成した後の追加タスクを実行できるようにします。例として、生成したモデルに追加のモデルを関係づけるコールバックを利用してみましょう。

$factory->afterMaking(App\User::class, function ($user, $faker) {
    // …
});

$factory->afterCreating(App\User::class, function ($user, $faker) {
    $user->accounts()->save(factory(App\Account::class)->make());
});

ファクトリステートのために、コールバックを定義することも可能です。

$factory->afterMakingState(App\User::class, 'delinquent', function ($user, $faker) {
    // …
});

$factory->afterCreatingState(App\User::class, 'delinquent', function ($user, $faker) {
    // …
});

ファクトリの使用

モデルの生成

ファクトリを定義し終えたら、機能テストかデータベースのシーディング(初期値設定)ファイルの中で、グローバルな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();

属性のオーバーライド

モデルのデフォルト値をオーバーライドしたい場合は、makeメソッドに配列で値を渡してください。指定した値のみ置き換わり、残りの値はファクトリで指定したデフォルト値のまま残ります。

$user = factory(App\User::class)->make([
    'name' => 'Abigail',
]);

Tip!! ファクトリを用いモデルを生成する場合は、複数代入の保護を自動的に無効にします。

モデルの保存

createメソッドはモデルインスタンスを生成するだけでなく、Eloquentのsaveメソッドを使用しデータベースへ保存します。

public function testDatabase()
{
    // 一つのApp\Userインスタンスを作成
    $user = factory(App\User::class)->create();

    // App\Userインスタンスを3つ生成
    $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 ($user) {
                $user->posts()->save(factory(App\Post::class)->make());
            });

関係を複数持つモデルを生成する場合は、createManyメソッドを使用します。

$user->posts()->createMany(
    factory(App\Post::class, 3)->make()->toArray()
);

リレーションと属性クロージャ

ファクトリ定義の中でモデルとのリレーションを指定することもできます。たとえば、Postを作成する時に、新しいUserインスタンスも作成したい場合は、以下のようになります。

$factory->define(App\Post::class, function ($faker) {
    return [
        'title' => $faker->title,
        'content' => $faker->paragraph,
        'user_id' => factory(App\User::class),
    ];
});

リレーションが定義するファクトリに依存する場合は、評価済みの属性配列を引数に取るコールバックを指定してください。

$factory->define(App\Post::class, function ($faker) {
    return [
        'title' => $faker->title,
        'content' => $faker->paragraph,
        'user_id' => factory(App\User::class),
        'user_type' => function (array $post) {
            return App\User::find($post['user_id'])->type;
        },
    ];
});

シーダの使用

機能テストでデータベースへ初期値を設定するために、データベースシーダを使いたい場合は、seedメソッドを使用してください。デフォルトでseedメソッドは、他のシーダを全部実行するDatabaseSeederを返します。もしくは、seedメソッドへ特定のシーダクラス名を渡してください。

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use OrderStatusSeeder;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * 新オーダー生成のテスト
     *
     * @return void
     */
    public function testCreatingANewOrder()
    {
        // DatabaseSeederを実行
        $this->seed();

        // シーダを1つ実行
        $this->seed(OrderStatusSeeder::class);

        // …
    }
}

使用可能なアサーション

Laravelは、多くのデータベースアサーションをPHPUnit機能テスト向けに提供しています。

メソッド 説明
$this->assertDatabaseCount($table, int $count); データベースのテーブルが、エンティティを指定量含むことをアサート
$this->assertDatabaseHas($table, array $data); 指定したデータが、テーブルに存在することをアサート
$this->assertDatabaseMissing($table, array $data); 指定したデータが、テーブルに含まれないことをアサート
$this->assertDeleted($table, array $data); 指定したレコードが削除されていることをアサート
$this->assertSoftDeleted($table, array $data); 指定したレコードがソフトデリートされていることをアサート

レコードの削除・ソフト削除のアサートに便利なよう、assertDeletedassertSoftDeletedヘルパへはモデルが渡せるようになっています。その場合、モデルの主キーを利用します。

たとえば、モデルファクトリをテストで使用する場合に、アプリケーションでデータベースからそのレコードが確実に削除されているかをテストするために、これらのヘルパへモデルを渡せます。

public function testDatabase()
{
    $user = factory(App\User::class)->create();

    // アプリケーションを呼び出す…

    $this->assertDeleted($user);
}

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる