Laravel 5.2 テスト

イントロダクション

Laravelはユニットテストも考慮して構築されています。実際、PHPUnitをサポートしており、最初から含まれています。アプリケーションのためにphpunit.xmlファイルも最初から準備されています。さらにフレームワークはアプリケーションを記述的にテストするために便利なヘルパメソッドも持っています。

ExampleTest.phpファイルがtestsディレクトリに提供されています。新しいLaravelアプリケーションをインストールした後、そのままphpunitをコマンドラインで実行し試してみてください。

テスト動作環境

テスト実行時にLaravelは、設定環境を自動的にtestingへセットします。そしてLaravelはセッションととキャッシュの設定ファイルをテスト環境で呼び出します。両方のドライバはテスト環境ではarrayにセットされます。つまりデータはテストを実行している間のみ存在しているということです。

必要であれば他のテスト設定環境を自由に作成することもできます。testing動作環境変数はphpunit.xmlの中で設定されています。テスト実行前には、config:clear Artisanコマンドを実行し、設定キャッシュをクリアするのを忘れないでください。

テストの定義と実行

新しいテストケースを作成するには、make:test Artisanコマンドを使います。

php artisan make:test UserTest

上記のコマンドにより、testsディレクトリ中に新しいUserTestクラスが設置されます。それから、いつもの通りPHPUnitを使ってテストメソッドを定義してください。テストを実行するには、ただターミナルでphpunitコマンドを実行するだけです。

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class UserTest extends TestCase
{
    /**
     * 基本的なテスト例
     *
     * @return void
     */
    public function testExample()
    {
        $this->assertTrue(true);
    }
}

注意: setUpメソッドを定義する場合はparent::setUpを確実に呼び出してください。

アプリケーションのテスト

Laravelはアプリケーションに送るHTTPリクエストの作成、出力の検査、さらにフォームの入力もスラスラと書けるAPIを提供しています。例としてtestsディレクトリに含まれるExampleTest.phpファイルを見てください。

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
    /**
     * 基本的な機能テストの例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->visit('/')
             ->see('Laravel 5')
             ->dontSee('Rails');
    }
}

visitメソッドはアプリケーションに対するGETリクエストを作成します。seeメソッドは指定されたテキストがアプリケーションから返されるレスポンスの中に存在することをアサートします。dontSeeメソッドは指定したテキストがアプリケーションレスポンスの中へ返されていないことをアサートします。これはLaravelで使用できる最も基本的なアプリケーションテストです。

アプリケーションとの関わり

もちろんシンプルに指定したレスポンスの中にテキストが現れるかアサートする以上のことが可能です。リンクのクリックとフォームの埋め込みを見てみましょう。

リンクのクリック

このテストではアプリケーションへのリクエストを作成し、帰ってきたレスポンス中のリンクを「クリック」、それから指定されたURIに到達することをアサートします。たとえば"About Us"のテキスト値を持つレスポンスの中のリンクがあると仮定しましょう。

<a href="/about-us">About Us</a>

では、このリンクをクリックし、正しいページに到達したことをアサートしましょう。

public function testBasicExample()
{
    $this->visit('/')
         ->click('About Us')
         ->seePageIs('/about-us');
}

フォームの埋め込み

Laravelはさらにフォームをテストするために多くのメソッドを提供しています。typeselectcheckattachpressメソッドはフォーム入力の全てを操作させてくれます。たとえば、アプリケーションの登録ページにこのフォームがあると考えてください。

<form action="/register" method="POST">
    {{ csrf_field() }}

    <div>
        Name: <input type="text" name="name">
    </div>

    <div>
        <input type="checkbox" value="yes" name="terms"> Accept Terms
    </div>

    <div>
        <input type="submit" value="Register">
    </div>
</form>

このフォームを完全に埋め、結果を確認するテストが書けます。

public function testNewUserRegistration()
{
    $this->visit('/register')
         ->type('Taylor', 'name')
         ->check('terms')
         ->press('Register')
         ->seePageIs('/dashboard');
}

もちろん、フォームにラジオボタンやドロップボックスのような他の入力が含まれていても、同様にそうしたフィールドを簡単に埋めることができます。フォーム操作メソッドのリストを見てください。

メソッド 説明
$this->type($text, $elementName) 指定したフィールドに「タイプ」します。
$this->select($value, $elementName) ラジオボタンかドロップダウンフィールドを「選択」します。
$this->check($elementName) チェックボックスフィールドを「チェック」します。
$this->uncheck($elementName) "Uncheck" a checkbox field.
$this->attach($pathToFile, $elementName) フォームにファイルを「添付」します。
$this->press($buttonTextOrElementName) 指定したテキストか名前のボタンを「押し」ます。

ファイル添付操作

フォームにfile入力タイプが含まれているならば、attachメソッドを使いフォームにファイルを添付できます。

public function testPhotoCanBeUploaded()
{
    $this->visit('/upload')
         ->type('File Name', 'name')
         ->attach($absolutePathToFile, 'photo')
         ->press('Upload')
         ->see('Upload Successful!');
}

JSON APIのテスト

さらにLaravelはJSON APIとそれらのレスポンスをテストするために、多くのヘルパも用意しています。たとえば、getpostputpatchdeleteメソッドは各HTTP動詞でのリクエストを発行するために使用します。また簡単にデータとヘッダをメソッドに渡すこともできます。手始めに、/userに対するPOSTリクエストを作成し、JSONフォーマットで指定指定した配列が返ってくることをアサートするテストを書いてみましょう。

<?php

class ExampleTest extends TestCase
{
    /**
     * 基本的な機能テストの例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->json('POST', '/user', ['name' => 'Sally'])
             ->seeJson([
                 'created' => true,
             ]);
    }
}

seeJsonメソッドは渡された配列をJSONに変換します。次にそのJSONが、JSONレスポンス全体のいずれかに現れるかを確認します。ですから、他のプロパティーがJSONレスポンスに含まれていたとしても、指定した部分が存在する限り、テストはパスします。

JSONと一致するか厳格に検証

指定した配列がアプリケーションから返されるJSONと厳格に一致するかを確認したい場合は、seeJsonEqualsメソッドを使用してください。

<?php

class ExampleTest extends TestCase
{
    /**
     * 基本的な機能テストの例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->json('POST', '/user', ['name' => 'Sally'])
             ->seeJsonEquals([
                 'created' => true,
             ]);
    }
}

JSON構造の合致

それには、seeJsonStructureメソッドを使い、ネストしたキーのリストを渡します。

<?php

class ExampleTest extends TestCase
{
    /**
     * 基本的な機能テストの例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->get('/user/1')
             ->seeJsonStructure([
                 'name',
                 'pet' => [
                     'name', 'age'
                 ]
             ]);
    }
}

上の例ではnameおよび、nameageを持つネストしたpetオブジェクトを受け取ることを期待しています。seeJsonStructureは、レスポンスが余計なキーを含んていても失敗しません。たとえば、petweight属性を持っていても、テストをパスします。

*を使い、戻ってきたJSON構造がリストを持っていること、そして各リストが最低でも持っている属性を指定することもできます。

<?php

class ExampleTest extends TestCase
{
    /**
     * 基本的な機能テストの例
     *
     * @return void
     */
    public function testBasicExample()
    {
        // 各ユーザはid、name、email属性を最低でも持っていることを宣言
        $this->get('/users')
             ->seeJsonStructure([
                 '*' => [
                     'id', 'name', 'email'
                 ]
             ]);
    }
}

*記述法はネストもできます。次の例の場合、JSONレスポンスの各ユーザは指定されている属性を持っており、各ユーザの各ペットも指定されている属性を持っていることを宣言します。

$this->get('/users')
     ->seeJsonStructure([
         '*' => [
             'id', 'name', 'email', 'pets' => [
                 '*' => [
                     'name', 'age'
                 ]
             ]
         ]
     ]);

セッション/認証

Laravelはテスト時にセッションを操作するたくさんのヘルパも提供しています。1つ目は指定した配列をセッションに保存するwithSessionメソッドです。これはアプリケーションのリクエストをテストする前に、データをセッションにロードしたい場合に便利です。

<?php

class ExampleTest extends TestCase
{
    public function testApplication()
    {
        $this->withSession(['foo' => 'bar'])
             ->visit('/');
    }
}

もちろん認証済みのユーザのようなユーザ状態をセッションへ保持するのは一般的です。actingAsヘルパメソッドは現在認証済みのユーザを指定する簡単な手段を提供します。たとえば、モデルファクトリでユーザを生成し、認証してみましょう。

<?php

class ExampleTest extends TestCase
{
    public function testApplication()
    {
        $user = factory(App\User::class)->create();

        $this->actingAs($user)
             ->withSession(['foo' => 'bar'])
             ->visit('/')
             ->see('Hello, '.$user->name);
    }
}

ユーザの認証にどのガードを使用するかを指定したい場合、actingAsメソッドの第2引数にガード名を渡します。

$this->actingAs($user, 'backend')

ミドルウェアの無効化

アプリケーションをテストするとき、いくつかのテストではミドルウェアを無効にするほうが便利だと気がつくでしょう。これによりミドルウェアに関わらずにルートやコントローラーのテストが可能になります。LaravelはシンプルなWithoutMiddlewareトレイトを用意しており、これを使えば自動的にテストクラスのミドルウェアを無効にできます。

<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
    use WithoutMiddleware;

    //
}

いくつかのテストメソッドだけでミドルウェアを無効にしたい場合は、withoutMiddlewareメソッドをテストメソッドの中で呼び出してください。

<?php

class ExampleTest extends TestCase
{
    /**
     * 基本的な機能テストの例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $this->withoutMiddleware();

        $this->visit('/')
             ->see('Laravel 5');
    }
}

カスタムHTTPリクエスト

アプリケーションに対してカスタムHTTPリクエストを作成し、完全なIlluminate\Http\Responseオブジェクトを取得したい場合は、callメソッドを使ってください。

public function testApplication()
{
    $response = $this->call('GET', '/');

    $this->assertEquals(200, $response->status());
}

POSTPUTPATCHリクエストの場合、そのリクエストに入力データの配列を渡すことができます。もちろん、このデーターはRequestインスタンスを使用し、ルートやコントローラーで使用できます。

   $response = $this->call('POST', '/user', ['name' => 'Taylor']);

PHPUnitのアサート

PHPUnitテスト用に、数多くの追加アサートメソッドをLaravelは提供しています。

メソッド 説明
->assertResponseOk(); クライアントのレスポンスがOKのステータスコードを受け取ることを宣言
->assertResponseStatus($code); クライアントのレスポンスが指定したコードであることを宣言
->assertViewHas($key, $value = null); レスポンスのビューに指定したデータが含まれていることを宣言
->assertViewHasAll(array $bindings); ビューが指定したデータのリストを持っていることを宣言
->assertViewMissing($key); ビューが指定したデータを含んでいないことを宣言
->assertRedirectedTo($uri, $with = []); クライアントが指定したURIへリダイレクトすることを宣言
->assertRedirectedToRoute($name, $parameters = [], $with = []); クライアントが指定したルートへリダイレクトされることを宣言
->assertRedirectedToAction($name, $parameters = [], $with = []); クライアントが指定したアクションへリダイレクトすることを宣言
->assertSessionHas($key, $value = null); セッションが指定した値を持つことを宣言
->assertSessionHasAll(array $bindings); セッションが指定したリストを持つことを宣言
->assertSessionHasErrors($bindings = [], $format = null); セッションにエラー情報があることを宣言
->assertHasOldInput(); セッションが直前の入力を持つことを宣言
->assertSessionMissing($key); セッションが指定したキーを持っていないことを宣言

データベースとの関わり

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');
    }
}

注意: このトレイトはデフォルトのデータベース接続をトランザクションでラップしているだけです。

モデルファクトリ

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

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => bcrypt(str_random(10)),
        'remember_token' => str_random(10),
    ];
});

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

もちろん追加のファクトリをModelFactory.phpに自由に追加できます。系統立てるためには各モデルのファクトリーファイルを追加したほうが良いでしょう。たとえば、database/factoriesディレクトリの中へUserFactory.phpCommentFactory.phpファイルを作成します。

複数のファクトリタイプ

時々、同じEloquentモデルクラスに対し、複数のファクトリを持ちたいことがあります。たとえば、たぶん通常のユーザに加えて「管理者(administrator)」のためのファクトリも用意したいでしょう。defineAsメソッドを使用すれば、そうしたファクトリを定義できます。

$factory->defineAs(App\User::class, 'admin', function ($faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => str_random(10),
        'remember_token' => str_random(10),
        'admin' => true,
    ];
});

基本のユーザファクトリから全属性を複製する代わりに、属性を取得するrawメソッドを使用できます。一度属性を取得したら、必要な追加の値を追加できます。

$factory->defineAs(App\User::class, 'admin', function ($faker) use ($factory) {
    $user = $factory->raw(App\User::class);

    return array_merge($user, ['admin' => true]);
});

テスト中のファクトリ使用

ファクトリを定義し終えたら、テストかデータベースのシーディング(初期値設定)ファイルの中で、グローバルなfactory関数を使用してモデルインスタンスを生成できます。では、モデル生成の例をいくつか見てみましょう。最初はmakeメソッドでモデルを生成し、データベースには保存しないでみましょう。

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

    // モデルをテストで使用…
}

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

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

さらにモデルのコレクションや指定したタイプのモデルも生成できます。

// App\Userインスタンスを3つ生成
$users = factory(App\User::class, 3)->make();

// App\Userの"admin"インスタンスを生成
$user = factory(App\User::class, 'admin')->make();

// App\Userの"admin"インスタンスを3つ生成
$users = factory(App\User::class, 'admin', 3)->make();

保存するファクトリモデル

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

public function testDatabase()
{
    $user = factory(App\User::class)->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;
        }
    ];
});

モック

イベントのモック

Laravelのイベントシステムを多用している場合、テスト中は静かにしてもらうか、特定のイベントモックしたいと思うでしょう。たとえばユーザ登録のテスト中は、たぶんUserRegisteredイベントのハンドラには全部起動してもらいたくないでしょう。起動されたら"Welcome"メールを送り…などされるでしょう。

Laravelはイベントハンドラを実行させずに期待するイベントが発行されたことを確認する、便利なexpectsEventsメソッドを用意しています。

<?php

class ExampleTest extends TestCase
{
    public function testUserRegistration()
    {
        $this->expectsEvents(App\Events\UserRegistered::class);

        // ユーザー登録コードのテスト…
    }
}

指定したイベントが発行されないことを確認するには、doesntExpectEventsメソッドを使います。

<?php

class ExampleTest extends TestCase
{
    public function testPodcastPurchase()
    {
        $this->expectsEvents(App\Events\PodcastWasPurchased::class);

        $this->doesntExpectEvents(App\Events\PaymentWasDeclined::class);

        // ポッドキャスト購入コードのテスト…
    }
}

全イベントハンドラの実行を停止したい場合は、withoutEventsメソッドを使用してください。

<?php

class ExampleTest extends TestCase
{
    public function testUserRegistration()
    {
        $this->withoutEvents();

        // ユーザー登録コードのテスト…
    }
}

ジョブのモック

場合によりアプリケーションへリクエストを作成し、コントローラーにより特定のジョブがディスパッチされることだけをテストしたい場合もあります。これにより、ジョブのロジックから切り離し、ルート/コントローラーを独立してテストできるようになります。もちろん、それからジョブ自身を別のテストクラスに分けてテストできます。

Laravelは便利なexpectsJobsメソッドを用意しており、期待しているジョブがディスパッチされたかを検査できます。しかしジョブ自身は実行されません。

<?php

class ExampleTest extends TestCase
{
    public function testPurchasePodcast()
    {
        $this->expectsJobs(App\Jobs\PurchasePodcast::class);

        // ポッドキャスト購入コードのテスト…
    }
}

注意: このメソッドはDispatchesJobsトレイトのdispatchメソッドか、dispatchヘルパ関数により起動されたジョブだけを検知します。直接Queue::pushで送られたジョブは検知できません。

ファサードのモック

テスト時、頻繁にLaravelのファサードの呼び出しをモックしたくなります。たとえば以下のようなコントローラーアクションを考えてください。

<?php

namespace App\Http\Controllers;

use Cache;

class UserController extends Controller
{
    /**
     * アプリケーションの全ユーザーリストの表示
     *
     * @return Response
     */
    public function index()
    {
        $value = Cache::get('key');

        //
    }
}

shouldReceiveメソッドを使用し、Cacheファサードへの呼び出しをモックできます。これはMockeryのモックインスタンスを返します。ファサードはLaravelのサービスコンテナにより管理され、依存解決されていますので、典型的な静的クラスよりもかなり高いテスタビリティーを持っています。例としてCacheファサードへの呼び出しをモックしてみましょう。

<?php

class FooTest extends TestCase
{
    public function testGetIndex()
    {
        Cache::shouldReceive('get')
                    ->once()
                    ->with('key')
                    ->andReturn('value');

        $this->visit('/users')->see('value');
    }
}

注意: Requestファサードはモックしてはいけません。テスト実行時にcallpostのようなHTTPヘルパメソッドへ望みの入力を渡してください。

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュを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)へ移動

その他

?

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