2012/01/26

[PHP]そろそろ真面目に自動テストしよう

PHPで自動テストを行うための、PHPUnitを実戦投入するためのセットアップ方法を書き並べます。
慣れれば一番手っ取り早い(と思っている)PHPUnitを使ってみます。



まず必要なのは、PEAR。これがないとダメ。
ubuntuならapt-getでインストールできます。既にインストールされている場合は次へ行きましょう。
sudo apt-get install php-pear

無事インストールが終わったら、まずはPEARのアップグレードをかけてみましょう。リポジトリのバージョンは最新ではないというのはいつものことです。
sudo pear upgrade --force PEAR

PEARのアップグレードはPEARコマンドで行います。rubygemやnpmと同じですね!

それでは、PHPUnitをインストールします。
pear config-set auto_discover 1
pear install pear.phpunit.de/PHPUnit

1行目はおまじないみたいなものです。無事にインストールできれば、とりあえず使える状態です。

がしかし、アナタ、1からテストコードを書くつもりですか? 恐ろしい・・・。おぞましい発想ですよ、それは!
世の中には、既に書いたコードからテストクラスのひな形を書き起こしてくれる便利なジェネレーターがあるんですからッ!
それがPHPUnit_SkeletonGeneratorです(まんまな名前)。
pear install phpunit/PHPUnit_SkeletonGenerator

これだけ用意しておけば、とりあえずは大丈夫でしょう! PHPUnitのオプションパッケージには、まだまだ便利なパッケージがあるようなので、いいのがあれば教えて下さい。

では、テストしてみましょう!
テスト対象のファイルは以下のものを使用します。
<?php
class test1{
 var $fuga;
 var $fugafuga;

 function setFuga($a){
  $this->fuga = $a;
 }
 
 function setFugafuga($b){
  $this->fugafuga = $b;
 }
 
 function fugafugafuga(){
  echo $this->fuga + $this->fugafuga;
 }
}

上のソースをtest1.phpというファイル名で保存します。PHPUnitを利用した自動テストではファイル名のネーミングも結構重要になってきますのでファイル名とその中に保存するクラスの名前は一緒にしたほうがいいです。
ex::)
class test1 → test1.php
class hogehoge → hogehoge.php

そして、phpunit-skelgenで自動出力したひな形のファイル名はそれぞれ以下のようになっています。
ex::)
test1.php → test1Test.php
hogehoge.php → hogehogeTest.php

それではテストケースのひな形を一気に作ります。
phpunit-skelgen --test test1

これだけでひな形は完成です。できたひな形はこんな感じです。
<?php
/**
 * Generated by PHPUnit_SkeletonGenerator on 2012-01-26 at 18:07:24.
 */
class test1Test extends PHPUnit_Framework_TestCase
{
    /**
     * @var test1
     */
    protected $object;

    /**
     * Sets up the fixture, for example, opens a network connection.
     * This method is called before a test is executed.
     */
    protected function setUp()
    {
        $this->object = new test1;
    }

    /**
     * Tears down the fixture, for example, closes a network connection.
     * This method is called after a test is executed.
     */
    protected function tearDown()
    {
    }

    /**
     * @covers test1::setFuga
     * @todo   Implement testSetFuga().
     */
    public function testSetFuga()
    {
        // Remove the following lines when you implement this test.
        $this->markTestIncomplete(
          'This test has not been implemented yet.'
        );
    }

    /**
     * @covers test1::setFugafuga
     * @todo   Implement testSetFugafuga().
     */
    public function testSetFugafuga()
    {
        // Remove the following lines when you implement this test.
        $this->markTestIncomplete(
          'This test has not been implemented yet.'
        );
    }

    /**
     * @covers test1::fugafugafuga
     * @todo   Implement testFugafugafuga().
     */
    public function testFugafugafuga()
    {
        // Remove the following lines when you implement this test.
        $this->markTestIncomplete(
          'This test has not been implemented yet.'
        );
    }
}

なんとなくうまくいっているようなので、一度テストを実行してみます。
phpunit test1  #コマンド入力
PHPUnit 3.6.9 by Sebastian Bergmann.



Time: 0 seconds, Memory: 2.25Mb

OK (0 tests, 0 assertions)

OKと出ました。良い感じです。しかし肝心のテスト内容は0。アサーションも0です。
まぁ何せひな形ですからね・・・。

2012/01/30 追記
phpunitで走らせてるソースが違うというコメントをいただきましたのでチェックしましたが、上の例ではテストしたいソースの方をテストにかけている状態なので、そりゃテスト0だわという話でした。

本来は
phpunit test1Test

でした。
ただ、これでも今回自動生成したひな形ではエラーが出るので、test1.phpをincludeするか、18行目の「$this->object = new test1;」をコメントアウトするかしないとダメなようです。なんでだろう? 元のクラスを利用する場面もあるので事前に準備してるだけかなぁ。また優しい人のコメントをお待ちしております! ありがとうございました!

2012/04/03 追記
元ファイルをincludeして、「$this->object」を利用してテスト対象クラスを呼び出すのがphpunit-skelgenのやり方みたいです。


以下は自分の備忘録のようなものですが、ファイル名とクラス名に一貫性を持たせられない事も往々にしてあるでしょう。
そういう時にもPHPUnitは対応できます。

phpunit-skelgen --test -- [クラス名] [ファイル名]

これで、[クラス名]+Test.phpというテストケースのひな形を生成することができます。
ただ、この方法で作ったテストケースは、ファイル名、クラス名、テストケースのファイル名に一貫性が無いので、テストケースの先頭でテスト対象のファイルをインクルードする必要があります(他の解決法がわかりませんでした)

2012/04/03 追記
テスト対象のファイルを常にincludeするのが良いようです。

デバッグコードを書くくらいなら、テストコードを書けという言葉があるくらい、劇的にバグ混入が減るPHPUnitを是非使ってみてください!


see also:
php-mcryptがyumでインストールできないときに幸せになる方法
PHPerが一瞬でC言語をマスターする方法
PHPのmysql系関数のまとめ
さくらインターネットの .mailfilter にハマる


2 件のコメント:

  1. $ phpunit test1
    でテストされないのは
    $ phpunit test1.php
    と指定されたのと同じだからです。

    $ phpunit test1Test
    と入力すれば以下のようになるはずです。


    PHPUnit 3.6.9 by Sebastian Bergmann.

    III

    Time: 0 seconds, Memory: 3.00Mb

    OK, but incomplete or skipped tests!
    Tests: 3, Assertions: 0, Incomplete: 3.

    返信削除
    返信
    1. こちらでも確認しましたが
      phpunit test1Test でした!

      そうした場合に今回のテストケースで発生する問題についても触れた内容をエントリに追加しました。

      中途半端な検証でエントリを出してしまって申し訳ないです。

      ご指摘ありがとうございました。

      削除