「privateだからテストしない」は思考停止
「privateメソッドにはテストを書くな」
有名な原則です。私も基本的には賛成です。
しかし、原則に縛られて、本当に必要なテストを諦めていませんか?
publicとprivateの違い
publicメソッドは、概念への窓口——外部に公開された安定したインターフェースです。当然、それに対するテストも安定します。
一方で、privateメソッドは内部実装の詳細です。リファクタリングで増減しますし、publicメソッドの変更にも影響を受ける不安定なものです。それに対するテストは当然、不安定なものとなります。
不安定なテストが招く実害
不安定なメソッドに対するテストは、メンテナンスコストが高くつきます。
リファクタリングしたいのに、テストの書き換えコストが重すぎて断念する。そんな状況は避けるべきです。
だから「privateメソッドはテストしない」が原則になるのです。
共用ロジックというジレンマ
複数のpublicメソッドから呼ばれる共用ロジック。public経由でテストすれば、同じ検証を複数箇所で繰り返すことになりかねません。
共用ロジックを直接テストすれば、一箇所で済みます。
しかし、先述の通り不安定なテストのコストは高くつきます。privateメソッドを直接テストするなら、本当にそれが必要か吟味しなければなりません。
「別クラスに切り出せ」への疑問
「テストしたいなら、別クラスのpublicメソッドにしろ」これはよく耳にする解決法です。
もちろん、設計上自然な分割であれば切り出すべきです。単一責任原則に沿った分割は歓迎されます。
ところが、テストを書くためだけにクラスを分割するのは話が違います。凝集度の高いクラスが分割される。privateだったロジックが、別クラスとはいえpublicになる。どちらも避けるべきことです。
テストの都合でプロダクトコードを歪めることは、本意ではないはずです。
privateなインターフェース
privateメソッドはインターフェースではない——普通はそう考えます。
しかし「独立してテストを書きたい」と強く思うprivateメソッドは、複数のpublicメソッドから共有される「内部概念」への窓口である可能性があります。
その内部概念が重要であれば、publicメソッドと同じ慎重さで設計する価値があります。その結果、そのprivateメソッドは「安定した内部インターフェース」になります。それに対するテストもまた、安定するでしょう。
これは「別クラスに切り出せ」と本質的には同じことをしています。整理する、ただし分割と公開はしない——それだけの違いです。
原則と例外
原則を守るのは大事です。でも、「なぜ」を理解せずに従うのは思考停止です。公開範囲だけを理由に、真っ当な判断を諦める必要はありません。
もっとも、これが当てはまるケースは多くありません。ほとんどのprivateメソッドは、ただの実装詳細、もしくは独立したテストを書く必要がない内部概念です。
例外が許されるのは、以下の条件をすべて満たす場合に限ります。
- 独立したテストを書きたいと強く思う
- クラスにすべきではない内部概念である
- コードを整理して安定したインターフェースにできた
一つでも欠ければ、そのテストは書くべきではありません。
また、これらの判断には経験が要ります。見極められる人がいて、コードレビューが機能している——その環境がなければ、全面禁止が正解です。
「privateだから」で止まるな。その先を考えろ。
Discussion