privateとprotectedメソッドをPHPUnitでテストする方法


JavaのJUnitでprivateメソッドをテストする方法はこちら

  • 環境 : PHP7.3

ReflectionClassを使ったprivateとprotectedメソッドを実行するメソッドを用意すると便利にテストできる

便利ポイント

  1. パラメータには、「実行するメソッド名」とその「メソッドの引数」を指定できるようにする。
    • privateもprotectedも同じやり方でできるのでメソッド名と引数を指定すれば実行できる
  2. パラメータでarray $paramとすることでメソッドの引数が1つでも複数でも指定できるようになる
    • メソッドの引数が3つの場合(private function A($a, $b, $c))は、doMethod('A', [$a, $b, $c]);となる
    • メソッドの引数が1つの場合(private function A($a))は、doMethod('A', [$a]);となる
  3. メソッドにしておけばprivateとprotectedのメソッドをいくつあっても簡単。な気がする。
privateとprotectedメソッドを実行するメソッド
    /**
     * privateメソッドを実行する.
     * @param string $methodName privateメソッドの名前
     * @param array $param privateメソッドに渡す引数
     * @return mixed 実行結果
     * @throws \ReflectionException 引数のクラスがない場合に発生.
     */
    private function doMethod(string $methodName, array $param)
    {
        // テスト対象のクラスをnewする.
        $controller = new FormController();
        // ReflectionClassをテスト対象のクラスをもとに作る.
        $reflection = new \ReflectionClass($controller);
        // メソッドを取得する.
        $method = $reflection->getMethod($methodName);
        // アクセス許可をする.
        $method->setAccessible(true);
        // メソッドを実行して返却値をそのまま返す.
        return $method->invokeArgs($controller, $param);
    }

例えばこんな感じ

FormController.php(テストしたいクラス)
<?php

namespace App\Http\Controllers;

class FormController extends Controller
{
    public function __invoke()
    {
        $param = array();
        $param['data'] = $this->ceateListData();
        return view('form', $param);
    }

    private function ceateListData(): array
    {
        $data = [
            // 内容はどうでも良いので省略
        ];
        return $this->addId($data);
    }

    protected function addId(array $data): array
    {
        if (count($data) !== 0) {
            // 内容はどうでも良いので省略
        }
        return $data;
    }
}
FormControllerTest.php(テストクラス)
<?php

namespace App\Http\Controllers;

use PHPUnit\Framework\TestCase;

class FormControllerTest extends TestCase
{
    /** @test */
    public function privateメソッドをテストする()
    {
        // privateメソッドを実行して結果を取得する.
        $list = $this->doMethod('ceateListData', []);

        $actual = gettype($list);
        $this->assertEquals('array', $actual);
    }

    /** @test */
    public function protectedメソッドをテストする()
    {
        $data = [(object)['data' => 'sample1'], (object)['data' => 'sample2']];

        // protectedメソッドを実行して結果を取得する.
        $list = $this->doMethod('addId', [$data]);

        foreach ($list as $v) {
            $this->assertIsInt($v->id);
        }
    }

    private function doMethod(string $methodName, array $param)
    {
        // 内容は先頭と同じ
    }
}