PHPUnitで単体テストを書いてみる〜privateメソッド編

2024.03.15 09:00
2024.03.15 10:29
PHPUnitで単体テストを書いてみる〜privateメソッド編

前回に引き続きPHPUnitでの単体テストについてです。
前回はpublicメソッドのテストだったのですが、今回はprivateメソッドです。

privateメソッドの場合はそのままではうまくいかなかったので、今回はprivateメソッドをテストする方法を調べてみました。

1. テストを書く

まずはテスト対象クラスです。
常にtrueを返すという、説明以外では使いようがないシンプル過ぎるクラスです。

// Hoge.php

<?php

class Hoge {
  private function _hoge(): bool
  {
    return true;
  }
}

これのテストはこんな感じ。

// HogeTest.php

<?php 

use PHPUnit\Framework\TestCase;

// テストしたいクラスをインポート
require_once './Hoge.php';

class HogeTest extends TestCase
{
    // ---------------------------------------------------------------------
    // 準備
    // ---------------------------------------------------------------------

    // インスタンス用の変数
    private $testClass;

    // privateメソッドを読み込むためのReflectionクラス用の変数
    private $reflection;

    public function setUp() :void{
        // 必ず親クラスのsetUpを呼んでおくこと
        parent::setUp();

        // テスト対象のクラスインスタンスを作成
        $this->testClass = new Validation();

        // privateメソッドを読み込むためにReflectionクラスを使う
        $this->reflection = new ReflectionClass($this->testClass);
    }

    // ---------------------------------------------------------------------
    // メソッドテスト
    // ---------------------------------------------------------------------
    public function test_check_true()
    {
        // Reflectionクラスを使ってprivateメソッドを読み込む
        $method = $this->reflection->getMethod('_hoge');

        // invokeメソッドを使って、privateメソッドを実行する
        $result = $method->invoke($this->testClass);

        // 検証
        $this->assertTrue($result);
    }
}

前段でやることが増えましたが、知ってしまえば簡単でしたね。

2. 引数があるprivateメソッドをテストする

では次は引数があるprivateメソッドのテストを書いてみます。
引数によって真偽を返すシンプルなつくりです。

// Hoge.php

<?php

class Hoge {
  private function _hoge(string $value): bool
  {
    if($value !== '') {
      return true;
    } else {
      return false;
    }
  }
}

テストはこんな感じになります。

// HogeTest.php

<?php 

use PHPUnit\Framework\TestCase;

// テストしたいクラスをインポート
require_once './Hoge.php';

class HogeTest extends TestCase
{
    // ---------------------------------------------------------------------
    // 準備
    // ---------------------------------------------------------------------

    // インスタンス用の変数
    private $testClass;

    // privateメソッドを読み込むためのReflectionクラス用の変数
    private $reflection;

    public function setUp() :void{
        // 必ず親クラスのsetUpを呼んでおくこと
        parent::setUp();

        // テスト対象のクラスインスタンスを作成
        $this->testClass = new Validation();

        // privateメソッドを読み込むためにReflectionクラスを使う
        $this->reflection = new ReflectionClass($this->testClass);
    }

    // ---------------------------------------------------------------------
    // メソッドテスト
    // ---------------------------------------------------------------------
    public function test_check_true()
    {
        // Reflectionクラスを使ってprivateメソッドを読み込む
        $method = $this->reflection->getMethod('_hoge');

        // invokeメソッドを使って、引数が1つのprivateメソッドを実行する
        $result = $method->invoke($this->testClass, true);

        // 検証
        $this->assertTrue($result);
    }

    public function test_check_false()
    {
        // Reflectionクラスを使ってprivateメソッドを読み込む
        $method = $this->reflection->getMethod('_hoge');

        // invokeメソッドを使って、引数が1つのprivateメソッドを実行する
        $result = $method->invoke($this->testClass, false);

        // 検証
        $this->assertFalse($result);
    }
}

invokeのclassを指定している部分の第二引数以降に値を指定すると、テストメソッドへの引数になります。こんな感じですね。

$result = $method->invoke($this->testClass, 引数1);

引数が複数ある場合でも簡単です。

$result = $method->invoke($this->testClass, 引数1, 引数2, 引数3);

こちらも知ってしまえばなんてことはないですが、
まさかpublicとprivateでこんなに違うとは、、という感じですね。

今回は以上です!