テストファーストで開発するなら手を動かしながら軽い気持ちで書きたい。
例えそのクラスがprivateメソッドで依存関係があっても。
実装
例えば文字列を加工する処理のテストをTestクラスで実装する。
// テストしたいメソッド
private function convertWord(string $keyword): string
{
$cv_word = trim($keyword);
$cv_word = mb_convert_kana($cv_word,'Kas');
return $cv_word;
}
public function testConvertWord()
{
// トリム処理 半角カナ->全角カナ(K)
$word = ' 全角カタカナ ';
$result = $this->convertWord($word);
$this->assertSame('全角カタカナ', $result);
// 全角英数字->半角英数字(a) 全角スペース->半角スペース(s)
$word = 'Aiueo123 ';
$result = $this->convertWord($word);
$this->assertSame('Aiueo123 ', $result);
}
移動方法
しかしこのメソッドは別のクラスで使うために書いてるので、メソッドを移動させる。
private $repository;
public function __construct(RepositoryInterface $repository)
{
$this->repository = $repository;
}
private function convertWord(string $keyword): string
{
$cv_word = trim($keyword);
$cv_word = mb_convert_kana($cv_word,'Kas');
return $cv_word;
}
移動させたらprivateメソッドなのでReflectionクラスを使ってprivateメソッドにアクセスできるようにして、
コンストラクタを起動せずにインスタンスを作成する。
public function testConvertWord()
{
$reflection = new \ReflectionClass(Service::class);
$method = $reflection->getMethod('convertWord');
$method->setAccessible(true);
$not_construct_service = $reflection->newInstanceWithoutConstructor();
// トリム処理 半角カナ->全角カナ(K)
$word = ' 全角カタカナ ';
// $result = $this->convertWord($word);
$result = $method->invoke($not_construct_service, $word);
$this->assertSame('全角カタカナ', $result);
// 全角英数字->半角英数字(a) 全角スペース->半角スペース(s)
$word = 'Aiueo123 ';
$result = $method->invoke($not_construct_service, $word);
$this->assertSame('Aiueo123 ', $result);
}
移動できたことを確認するためにphpunitを起動。
Time: 757 ms, Memory: 8.00 MB
OK (1 test, 2 assertions)
Process finished with exit code 0
まとめ
privateメソッドをテストするのは、クラス設計が悪い、別クラスやヘルパーとして切り出せとか賛否両論あるかもだけど、スコープを抑えたいとかどうしても設計を変更できない場合などもあると思うので知識として知っておいて良いと思う。まあ他言語だとReflectionクラスが無いとかあるし、後に設計変更したいと思っても、テストがあることで安全に変更できるので、まずは動くものとテストを書きたい。
(参考)
https://qiita.com/suin/items/a1c3d96050cb7e51424d
https://qiita.com/mpyw/items/b3d974c073e6484d51a4