CIUnit for 2.x でテストがパスするまで

いまさらながらTDDにも挑戦してみたい所存、ということでCIプロジェクトでのテストに付いて調べ回った。

CIにはビルドインでテストクラスがあるものの、あまり情報が無いのと、テストケースを実装ファイルに書いていく?ようなので、ちょっと手が出しにくい。

すると PHPUnit を持ちいる方法があり、テストケースを tests ディレクトリにまとめることが出来るとのこと。

必要なもの

  • PHPUnit
  • CIUnit for 2.x

PHPUnit のインストールは各環境にてテキトーに行う。PEARからインストールすると簡単。

Manual :: インストール

肝心要の CIUnit だが、記事執筆時点で 2.x に対応したものがリリースされていないため、それに対応したバージョンの fukata / CIUnit-for-CI2 を使わせていただきます。

こちらにもCI2対応版を作成されている方がいました。kenjis / my-ciunit

こっちの方が、testing/database.phpを読んでくれるようなので、設定ファイルの切り分けがしやすくてGOODです。

今回は fukata / CIUnit-for-CI2 を先に用いたため、こちらで進めます。ディレクトリ構成が多少異なるくらいで、導入後のテストの記述や実行に関して差はありません。

本家バージョンではありませんが、キチンと動作するのでなんの問題ありません。

Clone なり ダウンロードしたら、application/ に上書き(application/以下にtestsが、third_party/以下にCI_Unitが入ればOK)して導入完了です。

テスト用DBの設定

テスト用のデータベース定義を作ります。http://www.foostack.com/foostack/ の 2 minute setup を参考に、database.php を書き換えます。

//$active_group = 'default'; コレを以下に置き換える
$env_used = 'default'; //name of your development setting
if(defined('CIUnit_Version')){
  $env_used .= '_test';
}
$active_group = $env_used;

テストが実行され、DBが使用されるとき、$db[‘default_test’] が使用されるようにするための設定。

default設定の下あたりに $db[‘default_test’] として新しく設定を追記します。

// テスト用データベースの定義
$db['default_test']['database'] = 'hoge_test';
.
.

単純に _test という文字列をくっつけて環境を切り分けてるだけなので、任意の名前にしても良いでしょう。

ただし、データベース名だけは _test で終わる名前にしないといけません。もちろんテスト用のデータベースを作っておいてください。

kenjis / my-ciunit こちらを使う場合は、config/testing/database.php を作ります。

こちらの方が環境別の設定ファイルとして切り分けが出来るのできれいかもしれません。お好みで。

テスト駆動開発っぽくモデルを作る

この状態で tests/ に移動し、phpunit を実行してみます。おそらく赤い帯と以下のようなメッセージが出るかと思います。

...Table 'ci2_test.phone_carrier' doesn't exist
Failed to truncate the table phone_carrier

これは、サンプルで入っていたテストコードが実行されたが、それらが使用するテーブルが無いために発生したエラーです。そりゃそうですよね。と、いうわけで以下のテーブルを作成します。とりあえずこんな感じで。動けばよし。

CREATE TABLE `phone_carrier` (
  `name` varchar(255),
  `txt_address` varchar(255),
  `txt_message_length` int(11)
) DEFAULT CHARSET=utf8;

では実行してみましょう。今度はこんなエラーが。

[CIUnit] Error: 500 Message: Unable to locate the model you have specified: phone_carrier_model

内容は、テスト対象の phone_carrier_model.php が無いから出来ません、というようなことです。当たり前です。作ってませんから。

モデルを作る前に、テストの内容を確認してみます。いっぱい書いてありますが、testGetCarriersメソッドにだけ注目。

// tests/models/PhoneCarrierModelTest.php
public function testGetCarriers(array $attributes, $expected){
$actual = $this->_pcm->getCarriers($attributes);
$this->assertEquals($expected, count($actual));
}

このテストではテスト初期化時に $this->_pcm にPhoneCarrierModel のインスタンスをセットしているので、PhoneCarrierModel の getCarriers() に $attributes を渡して、その返り値を count() した値が $expected に一致するかどうか、というテストであることが分かります。

testGetCarriers に引数で渡される二つの変数はすぐしたの GetCarriersProvider() から渡されます。

この辺については PHPUnit のリファレンスを参照してください。このテストの場合は

$attributes = array( ‘name’ );
$expected = 5;

が渡されます。

では、モデルを作りましょう。というわけで以下をコピペ。

// models/phone_carrier_model.php
class Phone_carrier_model extends CI_Model{
  public function getCarriers( array $columns ){
    // ActiveRecordを使うので、database.php で $active_record = TRUE; にしてください。
    $this->db->select( implode( ',', $columns ) )->from( 'phone_carrier' );
    return $this->db->get()->result();
  }
}

getCarriers()のテストの内容が、名前(name)を渡し、5件(fixtureに書かれた全件)が返ってくるかどうかだったので、getCarriers()は指定したカラムの値を取ってくる仕様のメソッドであると判断しました。

モデルを作ったら、もう一度テストを走らせてみます。

OK (7 tests, 4 assertions)

緑の帯とともにこのようなメッセージが出たら、テストはパスです。

折角なのでもう少しテストを追加してみます。

// 結果の1つ目のオブジェクトが name プロパティを持っているか
$this->assertObjectHasAttribute( 'name', $actual[0], '結果の一つ目のオブジェクトに Name プロパティがない' );
// そのオブジェクトの name の値が "Verizon" であるか(fixtureの中身と一致しているか)
$this->assertEquals( 'name', $actual[0]->name, 'Name プロパティの値が Verizon ではない' );

assertメソッドについてはPHPUnitのリファレンスを参考にしてください。

最後のテスト追加は順序としては逆になってしまいましたが、こうやってまずテストを書き、そのクラスやメソッドの仕様を明確にしてから実装するのがTDD(テスト駆動開発)ってヤツですね。

controllerやhelperにも同様なサンプルが入っているので、まずはそれをいじりながら動きを確認するのが良さそうです、というかそうやって手探り状態でやってます。

PHPUnit が非常に高機能なのでもっといろいろ出来そうですが、こうやってassertメソッドをつらつら書いていくだけでも価値はあると思います。

とりあえず CI2 のプロジェクトにテストをする環境が構築できたので終了とします。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする