[PHPUnit] assertだけのテストから一歩先に進む

今まで、基本的にassertくらいしか使ったことなかった。

プログラム内部の動きまで確かめるテストコードに出会ったので
忘れないように調べたことを書いておく。

なので対象としては、プログラムをしっかり設計した後、それが
正しく動くかまでテストしたい中級者向けだと思われる。

メソッド(fuga)の呼び出し回数を確かめる

$hoge = $this->createMock(Hoge::class);

$hoge
    ->expects($this->once()) //1回呼ばれることを保証.複数回は、$this->exactly(n)
    ->method('fuga');

//$hogeを扱うオブジェクト
$operator = new Operator;

//内部的にHogeを一回のみ使う
$operator->execute();

createMockについて(phpunit5.4 ~)

指定したもの(クラス or インターフェース)のオブジェクトを作成する
→ 元クラスの __construct() や __clone() は実行されない

下記メソッドの設定と異なるオブジェクトを取得する場合は
getMockBuilder($type)を使う

    /**
     * Returns a test double for the specified class.
     *
     * @param string $originalClassName
     *
     * @return PHPUnit_Framework_MockObject_MockObject
     *
     * @throws PHPUnit_Framework_Exception
     *
     * @since Method available since Release 5.4.0
     */
    protected function createMock($originalClassName)
    {
        return $this->getMockBuilder($originalClassName)
                    ->disableOriginalConstructor()
                    ->disableOriginalClone()
                    ->disableArgumentCloning()
                    ->disallowMockingUnknownTypes()
                    ->getMock();
    }

メソッドの返り値をモックする

$hoge = $this->createMock(Hoge::class);

$hoge
    ->method('fuga')
    ->will($this->returnValue('fugasValue'));

引数が必要なメソッドに値をセットして実行できるようにする

$hoge = $this->createMock(Hoge::class);

$hoge
    ->method('fuga')
    ->with($this->equalTo('someValue')) // なんでもいい場合は、$this->anything()
    ->will($this->returnValue('fugasValue'));

オブジェクトの操作編

オブジェクトのprivateプロパティ値を取得する。(getter, setterが無いときなど)


namespace App\ValueObject;

class String
{
    public $length  = 5;
}
use App\ValueObject\String;

//*** test function内

// ReflectionProperty クラスのインスタンスを作成
$prop = new ReflectionProperty(String::class, 'length');

// String のインスタンスを作成
$obj= new String();

var_dump($prop->getValue($obj)); // 5

//*** test function内

同様にprivateなメソッドを動かしたいとき

<?php

namespace App\Tests\Utility;

trait InvokePrivateMethodBehavior
{
    /**
     * @param object $object     privateメソッドを実行するクラス
     * @param string $methodName
     * @param null   $param      実行するメソッドにパラメータが必要な場合は指定
     *
     * @return mixed
     */
    function invokePrivateMethod($object, $methodName, $param=null)
    {
        $reflection = new \ReflectionClass($object);
        $method = $reflection->getMethod($methodName);
        $method->setAccessible(true);
        $res = $method->invoke($object, $param);

        return $res;
    }
}

ユースケース

今担当しているプロジェクトでは、データの通り道が一本に抽象化されているので
処理に関わるオブジェクトが正しい回数呼ばれているか確かめていたりする。

ほかにも、バリデータが予想した回数呼ばれているかの確認にもモックは利用されている。

ひとこと

phpのhttpクライアントで有名なGuzzleさんは、デフォでモックの仕組みを用意していて優秀ですね。
assertだけでもいいのでテストは書いて、よいプロダクトライフを過ごしましょう!