Laravel 5.1 テスト

イントロダクション

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

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

テスト動作環境

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

必要であれば他のテスト設定環境を自由に作成することもできます。testing動作環境変数はphpunit.xmlの中で設定されています。

テストの定義と実行

新しいテストケースを作成するには、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->attach($pathToFile, $elementName) フォームにファイルを「添付」します。
$this->press($buttonTextOrElementName) 指定したテキストか名前のボタンを「押し」ます。

ファイル添付操作

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

public function testPhotoCanBeUploaded()
{
    $this->visit('/upload')
         ->name('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->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->post('/user', ['name' => 'Sally'])
             ->seeJsonEquals([
                 'created' => true,
             ]);
    }
}

セッション/認証

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

ミドルウェアの無効化

アプリケーションをテストするとき、いくつかのテストではミドルウェアを無効にするほうが便利だと気がつくでしょう。これによりミドルウェアに関わらずにルートやコントローラーのテストが可能になります。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(); セッションが直前の入力を持つことを宣言

データベースとの関わり

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に自由に追加できます。

複数のファクトリータイプ

時々同じ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());
            });

モック

イベントのモック

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

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

<?php

class ExampleTest extends TestCase
{
    public function testUserRegistration()
    {
        $this->expectsEvents(App\Events\UserRegistered::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メソッドにより起動されたジョブだけを検知します。直接Queue::pushで送られたジョブは検知できません。

ファサードのモック

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

<?php

namespace App\Http\Controllers;

use Cache;
use Illuminate\Routing\Controller;

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)へ移動

その他

?

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