CakePHP/モデルのテスト

CakePHP/モデルのテスト

バージョンと製造年月日

  • 2011-08-11
  • CakePHP 1.3.8

SimpleTestのインストール

CakePHPでテストするにはSimpleTestという外部のライブラリを使うことになる

SimpleTestのサイト→SimpleTest - Unit Testing for PHPからダウンロードして解凍、

app/vendors

のディレクトリに突っ込んでおしまい

app/vendors/simpletest/ファイルいろいろ

みたいになるように入れる。

テストクラスの準備

モデルをテストするにはまずそのテストを駆動するためのテストクラスを準備する。

今回はhoge.phpモデルをテストしたいので

app/tests/cases/models

以下にファイル

hoge.test.php

を作る。

そして、以下のようなCakeTestCaseを継承したクラスを実装する。 名前は「テスト対象モデル名TestCase」がいいだろう。

App::import('Model','Hoge');
class HogeTestCase extends CakeTestCase{
    var $fixtures = array('app.hoge');
    function startCase(){
        echo "■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■<br />";
        echo "HogeTestCase<br />";
        echo "■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■<br />";
    }
    function startTest($method){
        $this->Hoge = ClassRegistry::init('Hoge');
        echo "=================================================================<br />";
        echo "start {$method}<br />";
        echo "=================================================================<br />";
    }
 
    function testFindById(){
        $result = $this->Hoge->findById(1);
        $this->assertTrue($result['Hoge']['id'] == 1);        
    }
    function endTest(){
        unset($this->Hoge);
        ClassRegistry::flush();
    }
    function endCase(){
    }    
}

テストの実行

テストの実行。

webrootにある

test.php

にアクセスする。Hogeモデルのテストクラスをつくったので のようにTestCasesをクリックするとモデルごとのテストケース一覧が見えるようになると思う。

config/core.phpのdebugが0になっているとtest.phpにアクセスできないので見れない場合は確認しておく。

このモデルをクリックすることでテストクラスのメソッドが一気に実行される。失敗が一個でもああれば赤表示、全部成功でグリーン。

テストでの画面出力

自動的にテストするといっても試行錯誤する部分もあると思うので画面出力が欲しい場合がある。 そういう場合は単に

echo "hoge";

とechoしておけばechoの出力をCakeTestCase側がトラップしてくれてテストの結果出力と共に出してくれるようになる。

CakePHPにはグローバルな関数としてprというデバッグに便利な関数が用意されているのでこいつを利用すると楽に短く書ける

pr($hoge);

このprという関数はprint_rの出力をpreタグで挟み込んでくれるようなものでテスト画面では重宝する。

CakeTestCaseの各メソッド

startTestメソッド

startTestは各テストメソッドが実行される前の処理を記述する。つまりテストメソッドが10個あれば10回実行される。

モデルクラスのオブジェクト化等の初期化処理を共通的に書いておけばいいだろう。

テストの出力には切れ目が無いので、区切りを入れておくのもいいと思う。第一引数にテストメソッド名を受けるのでこれも利用できる。

    function startTest($method){
        $this->Hoge = ClassRegistry::init('Hoge');
        echo "=================================================================<br />";
        echo "start {$method}<br />";
        echo "=================================================================<br />";
    }

testHogeHogeメソッド

頭にtestとついたメソッドは実際に実行されるテストそのものとなる。

endTestメソッド

テストメソッド終了ごとに実行される処理を記述するstartTestと対になるメソッド。

オブジェクトの破壊処理とかを行う

    function endTest(){
        unset($this->Hoge);
        ClassRegistry::flush();
    }

テスト成功失敗判定処理

テストの判定はテストメソッド中で

$this->assertTrue(true);

のようなassert系メソッドを使う。 一度でもassertが失敗を返すとテストとしては失敗になる。

判定 記述 メモ
配列の要素の一致(順番無視)
$this->assertTrue($a == $b);
==演算子は配列の判定で順番を無視して入っている要素が一致すればtrueになる
配列の要素の一致(順番も一致)
$this->assertTrue($a === $b); 
===演算子は要素も順番も完全に一致すればtrueになる
NULLである
$this->assertNull($a); 
これは厳密にnullをチェックする。ゼロやfalseでは成功しない

データベースを使ったテスト

CakePHPはデータベースと連携したテストもサポートしている。

データの用意

まず突っ込む用のデータを用意する。データは↓のような場所に「任意の名前_fixture.php」というファイル名で作る。この名前は本当になんでもいい

app/tests/fixtures/hoge_fixture.php

中身はのようにCakeTestFixtureクラスを継承した↑の「任意の名前Fixture」クラスを作る

class HogeFixture extends CakeTestFixture {
    var $name = 'Hoge';
    var $import = array('model' => 'Hoge');
    var $records = array(
        array('id'       => 1,
              'name'     => 'うんこ',
              'age'      => '32',
              'created'  => '2011-08-12 00:00:00',
              'modified' => '2011-08-12 00:00:00',
        ),
        array('id'       => 2,
              'name'     => 'カレー',
              'age'      => '11',
              'created'  => '2011-08-12 00:00:00',
              'modified' => '2011-08-12 00:00:00',
        ),
    );
}

nameプロパティは名前・・・、

improtプロパティでmodelを指定してその設定を使って入れるデータの形式を取得している。つまりデータの形式をつどモデルが指し示すtableの定義から引き直してくれるのだ。なので入れるデータ形式をfixtureでしてしなくても大丈夫になっている。

このモデルが指し示すtableはCakePHP1.3では常にdatabase.phpに指定してあるdefaultを使うことになっていて、testの設定に空っぽのDBを用意しておけばそこにcreate tableまでしてくれてその後にdropまでやってくれるのだ。至れり尽くせり。

なので開発機の設定はdefaultも含めて全部テスト用と割りきって開発したほうがよい。環境による使い分けはデプロイ時にdababase.phpを取り替えてしまったほうが安全だろう。

そしてrecordsプロパティに連想配列の配列として値を書いておく

空っぽのテーブルが必要でデータは必要ないという場合でもfixtureがないとテーブル自体がないのでレコード空で作っておく必要がある。

フィールドの指定が必要な場合

ここでは設定をimportという形でもってきたが、CakePHPでは全てのモデルのidがauto_incrementでPKに指定されているという前提になっている。 つまりModelからimportした場合はかならずそのようにtableが構築される。マスタなどは開発者が任意のidとつけているということもあり、順番や値が重要だったりする。

そのようなid直指定の値をfixtureから流し込もうとするとauto_incrementの設定と被ってDupricateエラーが発生することがある。この場合fixtureで構築するテーブル構造を全部指定すれば回避できる。

そう、idのフィールドにkey⇒primaryを付けないのだ。

テストに組み込む

さっきのデータをテストに組み込むにはこのようにfixturesプロパティにぶち込む、複数読み込む必要がある場合は列挙する。

class HogeTestCase extends CakeTestCase {
    var $fixtures = array('app.hoge');
    //....
}

この時指定する名前は「app.任意の名前」だ。「app.」は決まり文句みたいなもんでアプリ側のfixtureから読み込みますよという印みたい。 あまり使う機会はないと思うがCakePHPそのもののテストをしたい時はcore.になる。

fixturesを用いた初期化はstartTestのさらに前の処理のbeforeという段階で行われるので実際のテストメソッド中で動的に切り替えたりすることはできない。

逆に言うとテストデータの状態をリセットしたい時にテストメソッドを分けるということだな。

    function startTest($method){
        $this->Hoge = ClassRegistry::init('Hoge');
    }

この時点でinitメソッドで作られたオブジェクトは接続先が自動的にtestに向けられている。なのでfixtureによって作られたデータでテストできるというわけ。

この設定は内部ではClassRegistryのconfigメソッドを使ってやられているのだが、このconfigメソッドがModelのコンストラクタで接続先を指定している。何もしなければやっぱりdefaultになる。

このコンストラクタをオーバーライドして適切にスーパークラスを呼び出していないような実装(呼び出さないとか、パラメータを受けない、パラメータを渡さない)をしている場合ここがうまく機能せずテストがムチャクチャになる

これで結構嵌った。

テスト用データベースの設定

CakePHPのテストは全自動で

  • テーブル生成
  • データ挿入
  • テスト終了
  • テーブル破壊

までやってくれるのでガワだけあれば特に後は何も必要ない。

テストによってfixtureを使い分ける

テストによってfixtureを使い分けたくなることもあるだろ。別にCakePHPのテストでは1モデルクラスに対して1テストケースクラスと規定しているわけじゃないし、テストケースクラスが対応するモデルを固定しているわけでもない。

なのでテストケースクラスをたくさん作って内部で呼ぶfixtureを変えてやればよいだけ。

テストを一気に実行する

テストをまとめて一気に実行するにはテストのグルーピング機能を使う

app/tests/group/my_all.group.php

のようなファイルを作って中に

class MyAllGroupTest extends TestSuite {  
    var $label = 'My All Group Test';  
    function myAllGroupTest() {
        //まとめて実行したい対象を死ぬほどズラズラ書く
        TestManager::addTestFile($this, APP_TEST_CASES.DS.'libs'.DS.'hoge');
        TestManager::addTestFile($this, APP_TEST_CASES.DS.'models'.DS.'piyo');
    }
 
}

このようにする。

幾つかの解説(公式でも!)でTestSuiteクラスではなくGroupTestクラスを継承しているものがあるが・・・

「グループするにはGroupTestを継承すると言ったな。あれは嘘だ」

ってな感じで嘘みたい。TestSuiteクラスを継承しましょう。このグループ機能ってアバウトに作られていてGroupTestを継承しようがaddTestFileでファイルがなかろうが別になんのエラーも出ないということ。なんだこれ

そして書いたらいつもの通り画面から実行できるわけだが・・・

App
    Test Groups

のほうを選ぶとさっきのやつが出てくるので選ぶと実行される

この中に

All tests

という項目があって全部のテストを一気に実行してくれるっぽいのだが順番はメチャクチャだしなんだか挙動も変なので使わないほうがいい

モデルのテストの注意

テストデータは必ず複数レコードで先頭レコードの値をサンプルに使わない

find系のメソッドはパラメーターのキーを間違えてもエラーにならない。単に無視されるだけ。 無視された場合SELECTならばWHERE句等が無いのと同じ挙動になる。

こういう場合fixtureの先頭レコードとかをテストの検証値に使うと条件がなくても出てきてしまうことがあるのでこのようなバグを検知できなかったりする。

自分ルール

fixtureを分ける

fixtureのファイル名は「名前_番号_fixture.php」とし

hoge_01_fixture.php

レコードの内容を明記する。

これはなんでもかんでもわけるって意味じゃなくて分ける必要になったら分けるということ、 問題がないなら極力1個のfixtureに入れる。

テストケース側ではfixture指定の部分でそのfixtureを使用する理由を明記する。

基本的なマスタデータのような最初に管理者が作ってしまったら明示的に増やすまで増えも減りもしないテーブルデータに関しては 番号省略のファイル名にする。

タグ

php/cakephp/test_of_model.txt · 最終更新: 2017-09-27 23:18 by 66.249.77.24