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

かけるヒトからできるヒト

プログラムを書ける人からプログラムが出来る人へなるために個人的にまとめるブログ

phpunitでprivateなstatic関数をテストする

最近新規開発のシステムでテスト駆動開発を試しています。

テストは絶対書いた方がいいとか、同じ工数ならテストが少ないほうがいいとか、色々言われていますが、個人的には新規開発のような、開発中に頻繁に内部のアルゴリズムが変わる状態の時こそテストを書いておくと、一瞬で変更箇所によるミスが見つかるので、最終的には早く仕上がると感じています。

 

プログラムを書いていると、当然privateな関数がどんどん増えていって、それらの関数についてもテストを書いておいた方が安心できる、というのが人情ってものです。

特に先ほど述べたような、アルゴリズムが頻繁に変わる時にすぐ該当箇所に気づくためには、private関数へのテストは必須です。

 

そこで、phpunitでprivateな関数をテストする方法を調べてみました。

 

候補

PHP5.4時代のprivateメソッドテスト手法 #php5_4 - 泥のように

上記のサイトがわかりやすくまとまっていました。

 

テスト用にクラスを継承して、継承したクラスからメソッドのアクセス権を上書きする

単純に考えればこんな方法もありそう。だけど、テストのためだけにわざわざクラス作るのはちょっと面倒だなぁという感じ。

 

Reflectionクラスから呼び出す

検索するとたくさん出てくる方法。

staticな関数についてのやり方があまりなかったので、今回はその方法をまとめようと思います。

 

php5.4以降ならばClosureを利用する

Closureは無名関数の呼び出しで、無名関数から呼び出されている時はprivateなメンバであろうといくらでもアクセスできてしまうという、すごいような怖いような機能らしいです。

比較的新しい機能なので、中々対応している環境が少ないのが難点です。

今回の開発案件もphp5.2なので残念ながらこちらの方式は使えず。

 

方法

ReflecionClassのinvokeArgsメソッドを使ってテストする。

ReflectionClassのインスタンスを作成して各種invokeメソッドを呼ぶ。

毎回インスタンス作成する処理を書くのがめんどくさいので、クラス化しておいた。

class Test_Util 
{
  // $class_obj : テストするクラスのオブジェクト(new CLASS_NAME())
  // $method_name : 呼び出すメソッド名のstring
  // $args : 呼び出すメソッドの引数
  //    array('args1' => 'hogehoge', 'args2' => 'fugafuga', ...)
  public static function invokeStaticMethod($class_obj, $method_name, $args)
  {
    $test_class = new ReflectionClass($class_obj);
    $method = $test_class->getMothod($method_name);
    // メソッドを外からアクセス可能にする
    $method->setAccessible(true);
    // invokeArgsメソッドの第一引数をnullにするとstaticメソッドを呼び出せる
    return $method->invokeArgs(null, $args);
  }
}

この関数をテストクラスから呼び出すと、privateな関数の結果を取得できるので、その値をテストする

class Logic_Hogehoge
{
  private static function hoge_add($hoge, $piyo)
  {
    return $hoge + $piyo;
  }
}

-------
// phpunitテスト
public function test_private_func()
{
  args = array(
    'args1' => 2, 'args2' => 3
  );
  $result = Test_Util::invokeStaticMethod(new Logic_Hogehoge(), 'hoge_add', args);
  $this->asserEquals($result, 5);
}

こんな感じでstaticでprivateなメソッドでもテストを書くことができました。