Laravel 4 でデータベースを使ったテストを書く際の Tips です。

自動テストでデータベースにアクセステストを書く際に大切なのが、データベーステーブルのデータをテストで想定された状態にしておくということです。テーブルの内容がテストを実行される度に異なると、ある時はテストが通って、ある時は通らないという状態になります。
この「想定された状態」をセットアップするために、フレームワークで用意されている Migration と Seeder を利用しています。
テストケースでマイグレーション実行
開発を進めていると、データベーススキーマを変更する場合があります。マイグレーションファイルを作成して、php artisan migrate コマンドで適用するのことになります。テスト用データベースについても適用する必要がありますが、php artisan migrate --env=testing を実行するのは、少し手間なので、テストケースで実行するようにしています。
artisan コマンドは、PHP コードでは、Artisan::call() で実行できるので、下記のようにすると、テストクラスでマイグレーションを実行することができます。
<?php
public function setUp()
{
parent::setUp();
Artisan::call('migrate');
}
ただ、マイグレーションは、全てのテストケースで毎回実行する必要が無いので、基底クラスである TestCase に下記のように実装して、初回のみ実行するようにしています。
<?php
class TestCase extends IlluminateFoundationTestingTestCase
{
protected static $databaseSetup = false;
/**
*
*/
protected function setUpDatabase()
{
if (static::$databaseSetup) {
return;
}
Artisan::call('migrate');
static::$databaseSetup = true;
}
/**
* Creates the application.
*
* @return SymfonyComponentHttpKernelHttpKernelInterface
*/
public function createApplication()
{
$unitTesting = true;
$testEnvironment = 'testing';
return require __DIR__ . '/../../bootstrap/start.php';
}
}
データベースを使うテストクラスでは、下記のように setupDatabase メソッドを実行して、マイグレーションを実行します。(実際にマイグレーションが実行されるのは初回のみ)
<?php
class FooTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->setUpDatabase();
}
}
フィクスチャとして Seeder を使う
Laravel には、テーブルのデータを一括登録する仕組みとして Seeder があります。テーブルへの登録処理を書いておくと、php artisan db:seed コマンドでデータを登録することができます。主にアプリケーションで必要なマスタデータや初期データを投入するのに使われますが、これをテスト時のフィクスチャとして利用します。
Seeder クラスは、app/database/seeder 以下に配置するのが通常なのですが、フレームワークとしては、オートローダーで読み込むことができれば、どこに配置しても構いません。
テスト用の Seeder クラスは、テストケースと密接な関係にあり、テストに応じたレコードを用意する必要があるので、同じ PHP ファイルに定義しています。クラス名は、テストケースクラス名の後ろに Seeder を付けています。こうすれば、どのテストケースでも、同じファイルにある Seeder クラスを __CLASS__ . 'Seeder'で参照できます。
<?php
class FooTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->setUpDatabase();
$this->seed(__CLASS__ . 'Seeder');
}
}
class FooTestSeeder extends Seeder
{
public function run()
{
Eloquent::unguard();
Item::truncateAll();
Item::create(
[
'id' => 1,
'name' => '商品1',
'price' => 100,
]
);
}
}
Item::truncateAll() は、PostgreSQL 環境で、truncate を実行するために独自に実装したメソッドで、下記のような実装になっています。TRUNCATE 文に RESTART IDENTITY CASCADE を付けることで、テーブルのシーケンス値のリセットと、このテーブルを外部参照しているテーブルを同時に削除することができます。
<?php
class AppModel extends Eloquent
{
/**
* for test
*/
public static function truncateAll()
{
$table = (new static)->getTable();
$sql = 'TRUNCATE ' . $table . ' RESTART IDENTITY CASCADE';
DB::table($table)->getConnection()->statement($sql);
}
}
複数のテストケースで共有する場合は、app/database/seeder に配置して、$this->seeder('TestCommonSeeder') などで実行します。
さいごに
冒頭に書きましたが、データベースを利用したテストでは、事前に想定した環境を作っておくことが肝になります。
Seeder をフィクスチャとして使うことで、アプリケーションの機能を使うことなく、テストに必要な状況を作り出すことができます。テストの中で、テスト対象以外のアプリケーションコードを実行すると、何をテストするためのものなのかが、ぶれてしまうので、環境構築はフレームワークやライブラリで用意された機能のみでシンプルに実現するのが良いでしょう。

