読者です 読者をやめる 読者になる 読者になる

肉とビールとパンケーキ by @sotarok

少し大人になった「肉とご飯と甘いもの」

PHPUnit とかで assertTrue とか面倒だから at とかにするライフハック

いや,多分ハックしてるのはライフじゃないとおもうんですけど,まーライフハックしつつハックするってかんじで.

まーテスティングフレームワークっていろいろあるとおもうんですけど,PHPUnitがなんかカッコイイから使ってみている最近です.


で,やっぱり色々面倒なのは, assertTrue とかですよね?
最小のテストコードって以下のようになってると思うんですよ.(マニュアルから抜粋)

<?php
require_once 'PHPUnit/Framework.php';
 
class StackTest extends PHPUnit_Framework_TestCase
{
    public function testPushAndPop()
    {
        $stack = array();
        $this->assertEquals(0, count($stack));
 
        array_push($stack, 'foo');
        $this->assertEquals('foo', $stack[count($stack)-1]);
        $this->assertEquals(1, count($stack));
 
        $this->assertEquals('foo', array_pop($stack));
        $this->assertEquals(0, count($stack));
    }
}

$this->assertEquels とか省略したい

で,この assertEquals とかいちいち打ってられないから,とりあえず ae とかにエイリアスを作りたいと思います.

<?php

require_once 'PHPUnit/Framework.php';
 
class TestBase extends PHPUnit_Framework_TestCase
{
    protected $aliases = array(
        'at' => 'assertTrue',
        'af' => 'assertFalse',
        'ae' => 'assertEquals',
        // ...
    );  

    public function __call($name, $args)
    {   
        if (array_key_exists($name, $this->aliases)) {
            call_user_func_array(array($this, $this->aliases[$name]), $args);
        }  
    }      
}

こんなものを用意してあげて,これを継承してテストケースをつくれば,以下のようにできますね.alias を 連想配列で定義してあげるのは手動ですけど ... まあ一度書いてしまえばらくちんですね.

<?php

class StackTest extends TestBase
{
    public function testPushAndPop()
    {
        $stack = array();
        $this->ae(0, count($stack));
 
        array_push($stack, 'foo');
        $this->ae('foo', $stack[count($stack)-1]);
        $this->ae(1, count($stack));
 
        $this->ae('foo', array_pop($stack));
        $this->ae(0, count($stack));
    }
}

うん,少しマシになりました.

もっと省略したい

でもこれも使ってると, $this->ae とかが鬱陶しくなってきます.$this-> とかいらないですよねえ.
というわけで,さらに以下のように,関数を定義します.debug_backtrace でオブジェクトをとれるのが素敵ですね.

<?php

function ae ($v1, $v2) {
    $bt = debug_backtrace();
    $bt[1]['object']->assertEquals($v1, $v2);
}

function at ($v) {
    $bt = debug_backtrace();
    $bt[1]['object']->assertTrue($v);
}

function af ($v) {
    $bt = debug_backtrace();
    $bt[1]['object']->assertFalse($v);
}

うおおおこれをassert関数分用意するのはめんどくさいいいメタプログラミングしたいところですね!!!
と思ったけどまーこれも一度書いてしまえばいい話なので,ヨシとしましょう.

これによって,テストケースでは,以下のようにかけるようになりました.

<?php

class StackTest extends TestBase
{
    public function testPushAndPop()
    {
        $stack = array();
        ae(0, count($stack));
 
        array_push($stack, 'foo');
        ae('foo', $stack[count($stack)-1]);
        ae(1, count($stack));
 
        ae('foo', array_pop($stack));
        ae(0, count($stack));
    }
}


うん,これならいっぱいテスト書きたくなりますね.

DocTest 使えばいいと思うよ

といってしまったらもう返す言葉もない.

いや,でも PHPUnit 結構いいですよ

アノテーションつかえて,スーパーグローバルのバックアップをとったり,依存の定義も簡単にできるし.
というわけで Let's Testing!