わすれっぽいきみえ

みらいのじぶんにやさしくしてやる

17日目: codeception使ってみた

Codeception

簡単にいうとPHPのテスティングフレームワーク。業務で初めて使った。
本当はAcceptance Testが書けることについて書きたいところだが、ここはまだ書き慣れてないのでこの記事には載せてない。

Db - Codeception - Documentation

個人的にはこのDb Modulesがとても良かった。

PHPUnitでdatabase周りのテストを書くのが個人的に超面倒くさくて

PHPUnit Manual – Chapter 8. Database Testing

に書いてあるfixtureの設定をやってた。

上記ページに書いてある例をちょっとだけいじって引用すると

<?xml version="1.0" ?>
<dataset>
    <table name="users">
        <column>id</column>
        <column>content</column>
        <column>name</column>
        <column>created</column>
        <row>
            <value>1</value>
            <value>Hello buddy!</value>
            <value>joe</value>
            <value>2010-04-24 17:15:23</value>
        </row>
        <row>
            <value>2</value>
            <value>I like it!</value>
            <null />
            <value>2010-04-26 12:14:20</value>
        </row>
    </table>
</dataset>

yamlですらなくxmlで本当につらかった。
fixtureが別のファイルに書かれてるとテストと設定ファイルの両方を見ながら「ああ、こういうテストがしたいのね」を理解しなきゃいけないのが個人的につらく、「いやテストごとに使うデータが違うんだからfixture分けるの当たり前じゃない?」みたいなことを言われると「だったら別ファイルで管理するの無駄じゃない?テストごとにどんなデータをテストしたいのか初期条件を直接書けよ」みたいな気持ちになってた。

それが

$I->haveInDatabase('users', ['id' => '1', 'content' => 'Hello buddy!', 'name' => 'joe', 'created' => '2010-04-24 17:15:23']);
$I->haveInDatabase('users', ['id' => '2', 'content' => 'I like it!', 'name' => 'nancy', 'created' => '2010-04-26 12:14:20']);

これで直接fixtureが書ける。

PHPUnitでも

    public function testIncludeFilteredGuestbook()
    {
        $tableNames = array('guestbook');
        $dataSet = $this->getConnection()->createDataSet();

        $filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataSet);
        $filterDataSet->addIncludeTables(array('guestbook'));
        $filterDataSet->setIncludeColumnsForTable('guestbook', array('id', 'content'));
        // ..
    }

と書けば書けるんだがこの書き方は冗長だなぁと思っていて、あまりイケてないと思う。

あとDbの中に入ってるものの確認に

$I->seeInDatabase('users', ['name' => 'nancy']); // 存在することをテストする
$I->dontSeeInDatabase('users', ['name' => 'Davert']);  // 存在しないことをテストする

これが使えるのも楽だった。ある特定カラムがちゃんと変更されてるかなどを見るのに使える。

あと例えば複数テーブルのレコードを一気に作成して、外部キー参照にインサートされたばかりのレコードが使われることがある場合とかは

$I->assertSame($I->grabFromDatabase('user_lists', 'user_id', ['list' => 'hoge']), $I->grabFromDatabase('users', 'id', ['name' => 'jane']));

こういう書き方ができる。
直接id指定して assertSame すればいいじゃんと思うかもしれないが、idはauto incrementが多いので、直前に入ったidがわかってないと次にinsertされたレコードのidがいくつになるかわからない。上のような書き方にしておくと、idの値そのものはわからなくてもリレーションが正しいレコードが作成されたかは確認することができる。
リレーションが正しいかどうかを検証するのに、この例がいい例か?と言われるとそれは微妙な気がしている。

Annotationsも良くて、PHPUnitだとあるテストクラスの中のすべてのテストケースに対して @after なりが適応されてたのが、テストケースごとに適応したい @after のメソッドが呼び出せるのが良かった。

例を書くと

// PHPUnitだと
class HogeTest extends PHPUnit_Framework_TestCase
{

    /**
     * @after
     */
    public function tearDown()
    {
        // このメソッドがこのテストクラスに記載されてるすべてのテストケースで呼び出される
    }
}
// codeceptだと
class HogeCest
{
    public function tearDown()
    {
        // このメソッドを呼ぶかどうかはテストケース次第
    }

    /**
     * @after tearDown
     */
    public function testHoge($I)
    {
        // このテストケースではtearDownが呼ばれる
    }

    public function testFuga($I)
    {
        // このテストケースではtearDownが呼ばれない
    }
}

こんな感じ。

他にも機能はたくさんあるが、とりあえず便利なので使ってみるといいかも。