CodeIgniter3 x PHPUnit で private methodをテストする

privateメソッドのテストは、複雑なデータ構造を扱う時など開発途中で時々どうしても書きたくなります。
その外のpublicのテストでカバーできたら、private のテストは捨てちゃったりしますが。
 
Rubyだと send 使って何でもできるのに、と思いつつ調べつつPHPでテスト書いてみました。

PHPだと?

PHP5.4時代のprivateメソッドテスト手法 #php5_4: Architect Note
こちらが詳しいのですが
5.3以前だとReflectionMethodを使う方法がありますが、
5.4以降だとClosureが使えます。
5.3のClosureだと$thisが渡せなくて辛い想いを経験したことがありますが
5.4以降で対応してくれたようです。
($thisのcontextを変えれえないのはclosureとしては辛い。。)

Closure::bind を使った例

最近CodeIgniter3 x PHPUnitでテストを書いているので
ちょっと試してみました。

CodeIgniter3 での PHPUnitkenjis/ci-phpunit-test こちらを利用しています。
関連: CodeIgniter 3 で PHPUnit を使ってテストをする - rochefort's blog
 
_some_private_method() をテストする例。

<?php

class Some_library_test extends TestCase {

    public function setUp()
    {
        $this->CI =& get_instance();
        $this->CI->load->library('Some_library');
        $this->obj = $this->CI->some_library;
    }

    /**
   * _private_some_method のテスト
   *
   * @dataProvider some_provider
   */
    public function test_private_some_method($expected, $param)
    {
        $cl = Closure::bind(function($param)
        {
            return $this->obj->_some_private_method($param);
        }, $this, 'Some_library');

        $actual = $cl->__invoke($param);
        $this->assertEquals($expected, $actual);
    }
    
    /**
   * private_some_method のデータプロバイダ
   *
   * @return   array [$expected, $params]
   */
    public function some_provider()
    {
        return [
            ['expected', 'params'],
            ['expected2', 'params2']
        ];
    }
}

ポイントは、データプロバイダを使うとこです。
関連: PHPUnitいいね - rochefort's blog
こいつを使うことでいろんなデータパターンを利用したテストが(割と)すっきりかけます。
 
Closure作って __invokeで実行しているのですが
__invoke が引数を取れるので、データプロバイダの値をclosureの中に渡して使えます。

結論

Closureいいね!

See Also

CodeIgniter 3 で PHPUnit を使ってテストをする - rochefort's blog
PHPUnitいいね - rochefort's blog