CakePHP 1.2の単体テスト作成にはbakeが便利 (後編)
こんにちは、シーブレインのtanakaです。前回の記事でbakeを使ってテストケースのひな形を生成しました。今回は、生成されたコードを変更しながら、単体テスト実行の仕組みについて理解していきます。
Fixtureを書き換える
bakeで生成されたファイルのうちFixturesを見ていきます。生成されたコードは以下の通りです。
<?php
/* SVN FILE: $Id$ */
/* Entry Fixture generated on: 2009-02-05 02:02:55 : 1233767515*/
class EntryFixture extends CakeTestFixture {
var $name = 'Entry';
var $table = 'entries';
var $fields = array(
'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
'title' => array('type'=>'string', 'null' => false, 'default' => NULL),
'body' => array('type'=>'text', 'null' => false, 'default' => NULL),
'created' => array('type'=>'datetime', 'null' => false, 'default' => NULL),
'modified' => array('type'=>'datetime', 'null' => false, 'default' => NULL),
'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
);
var $records = array(array(
'id' => 1,
'title' => 'Lorem ipsum dolor sit amet',
'body' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida,phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam,vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit,feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.',
'created' => '2009-02-05 02:11:55',
'modified' => '2009-02-05 02:11:55'
));
}
?>
詳しい解説は公式マニュアルに譲るとして、ここではコードを書き換えます。EntryFixtureクラスには4つのフィールドが定義されていますが、まず$fieldsに注目します。この $fields、create文で定義したテーブルの構造の情報をコピーしたようなデータになっていますが、bakeは開発用DBからスキーマの情報をコピーしてフィクスチャを生成します。そして単体テストを実行するときは、実行する毎に、テスト用DBにテーブルを生成します。フィクスチャは要するにテスト用のデータを用意する仕組みです。さて、この$fieldsを以下のように書き換えて実行してみます。
var $import = 'Entry';
書き換えて実行してもテスト結果は変わらないと思います。テストの表向きの動作は変化しません。何が違うかというと、$importを定義すれば、毎回、開発用DBからスキーマ情報を取り出して、テスト用DBにテーブルを作るという動作をします。もし、テーブルのスキーマを変更した場合にフィクスチャの$fieldsも修正するという手間が省けるわけです。
その下に$recordsというフィールドがあります。これはテスト開始時にDBに保存されている初期データを定義します。モデルの単体テストを作ると、テスト後にDBの状態が変わるのが普通なので、毎回同じデータでテストできるようにする必要があります。ここにいくつかデータを増やしてみます。
var $records = array(
array(
'id' => 1,
'title' => 'Lorem ipsum dolor sit amet',
'body' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida,phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam,vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit,feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.',
'created' => '2009-02-05 02:11:55',
'modified' => '2009-02-05 02:11:55'
),
array(
'id' => 2,
'title' => 'デベロッパーテスティング',
'body' => 'bakeで開発者テストが加速するかも?',
'created' => '2009-03-01 02:11:55',
'modified' => '2009-03-01 02:11:55'
),
array(
'id' => 3,
'title' => 'フィクスチャでテストデータの用意が楽になりますよ',
'body' => 'スキーマの取り込みも簡単ですよ',
'created' => '2009-03-01 11:02:09',
'modified' => '2009-03-01 11:02:09'
),
);
初期データが追加できたら、もう一度テストを実行してみましょう。文法エラーなどがあれば、すぐチェックできるのでエラーメッセージがでたらすぐ修正します。次にテストケースを書き換えていきます。
テストケースを書き換える
テストデータがフィクスチャで用意できたら、次はテストケースです。bakeで生成されたテストケースは以下の通りです。
<?php
/* SVN FILE: $Id$ */
/* Entry Test cases generated on: 2009-02-05 02:02:55 : 1233767515*/
App::import('Model', 'Entry');
class EntryTestCase extends CakeTestCase {
var $Entry = null;
var $fixtures = array('app.entry');
function startTest() {
$this->Entry =& ClassRegistry::init('Entry');
}
function testEntryInstance() {
$this->assertTrue(is_a($this->Entry, 'Entry'));
}
function testEntryFind() {
$this->Entry->recursive = -1;
$results = $this->Entry->find('first');
$this->assertTrue(!empty($results));
$expected = array('Entry' => array(
'id' => 1,
'title' => 'Lorem ipsum dolor sit amet',
'body' => 'Lorem ipsum dolor sit amet, aliquet feugiat. Convallis morbi fringilla gravida,phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin venenatis cum nullam,vivamus ut a sed, mollitia lectus. Nulla vestibulum massa neque ut et, id hendrerit sit,feugiat in taciti enim proin nibh, tempor dignissim, rhoncus duis vestibulum nunc mattis convallis.',
'created' => '2009-02-05 02:11:55',
'modified' => '2009-02-05 02:11:55'
));
$this->assertEqual($results, $expected);
}
}
?>
数十行のコードが出ましたが、この中でまさにテストをしている部分はtestEntryInstanceメソッドとtestEntryFindメソッドです。この説明は省略して、ここではテストを追加します。
function testEntryCount() {
$count = $this->Entry->find('count');
$this->assertEqual(3, $count);
}
短いコードです。これで何をテストしているかというと、entriesテーブルのデータを全件カウントしたら3件になるはずだ、ということをテストしています。先ほど、フィクスチャで初期データを3件用意しましたので、このテストコードでチェックします。
テストを実行して、『1/1 test cases complete: 4 passes, 0 fails and 0 exceptions.』と表示されたらテストに全件通ったことになります。passの数が1つ増えました。
テストメソッド以外の部分を調べます。
var $fixtures = array('app.entry');
ここではフィクスチャを読み込みます。テーブルを生成して初期データを挿入しています。
function startTest() {
$this->Entry =& ClassRegistry::init('Entry');
}
startTestメソッドは特別で、すべてのテストメソッドの前に実行されます。ここではEntryモデルの状態がつぎのテストメソッドに影響しないように毎回初期化しています。逆にendTestメソッドも定義でき、すべてのテストの後に実行されるコードを指定できます。
このテストケースの全体的な流れは、1,フィクスチャを読み込む→2,startTestメソッドが実行される→3,test****というメソッドが実行される→4, test***の個数だけ2と3が繰り返されるという風になります。
まとめ
- bakeで生成されるフィクスチャとテストケースを修正しながら解説いたしました。
- フィクスチャでDBの初期状態を定義します。
- フィクスチャで$importを定義するとスキーマの変更に自動的に対応できます。
- テストケースでモデルの機能をチェックします。
- すべてのメソッドの前処理はstartTestメソッドで行います。
解説しなかったこと
- モデル以外のテスト, Webテスト
- テスト駆動開発(TDD)
- DBを使わないテスト
- その他もろもろ