Yabu.log

ITなどの雑記

テストのためだけのコード変更は許されるのか?

レガシーコード改善ガイドに以下のような一節がありました。

私はテストが簡単に書けるなら、変数をpublicにすることでカプセル化が壊れても通常は気にしません。

私はEffective Javaに強く影響を受けているのでメンバを書くときは極力公開性を最小にコーディングする。 しかし困るのはユニットテストのときで、privateなメンバのテストはリフレクションを使ってテストコードからアクセスしていた。

可変性*1・公開性*2を最小にする、というこだわりを持っているので上記の引用の記述には少し驚いた。

そもそもテストコード内でのアクセス出来ないメンバはどう対処するのが最適なのか、少し調べてみた。

https://qa.atmarkit.co.jp/q/2784

短くまとめると、プライベートなメソッドのテストを書く必要は 無い と考えています。ほとんどのプライベートメソッドはパブリックメソッド経由でテストできるからです。プライベートメソッドは実装の詳細であり、自動テストのターゲットとなる「外部から見た振る舞い」ではありません。

こちらの方*3の意見は非常に説得力がある。 確かにブラックボックステストとしてユニットテストを作成するなら、privateメソッドのテストは書く必要がない。

一方でこの方はレガシーにも触れている。

レガシーコード(テストコードの無いコード)に対する「仕様化テスト(Characterization Test)」を書いているような状況では、リフレクションは唯一の、かつ強力な手段になります。プライベートメソッドにテストを書くことのデメリットを理解しつつ、黒魔術の強力さを堪能しましょう。

機能追加の際に、理想的なテストを書いてしまうと、既存バグが発見され、そちらの対応に追われるかもしれない。 バグ修正よりも現在の変更作業を優先するために、一旦既存バグを含めた現在の挙動を保護するテストを作成する。*4

ほぼ全てのレガシーシステムでは、「システムがどのようにうごくべきか」よりも、「実際にどのように動いているか」のほうが重要です。*5

仕様化テストはレガシーコードに対処し始めた初期段階のプロセスといえるかもしれない。

調べた知見を自分なりにまとめると

まともにやっている新規開発*6ではprivateメソッドのテストなんて書く必要はないけど、 設計が破綻しているレガシーコードに絡んだsingleton*7などはテストの観点から見ると有害なので、公開性をどれくらい壊すか、というところのトレードオフを考えながらコード変更やリフレクションという黒魔術を使おう、ということになる。

調べながら・考えながら読んでいるので読むペースは遅いが、どんどん内容が面白くなってきた

参考:レガシーコード改善ガイド 第9章

*1:Effective Java項目15:可変性を最小限にする

*2:Effective Java項目13:クラスとメンバーへのアクセス可能性を最小限にする

*3:ユーザー名を見たらt_wadaさんだった…

*4:もちろんこれはゴールではない。既存バグの修正よりも、機能追加を優先したというだけであり、既存バグは別に対処しなければならない。

*5:レガシーコード改善ガイド13.1仕様化テスト

*6:新規開発のすべてがまとも、というわけではない

*7:文中ではsingletonのコンストラクタの公開性をprivate→protectedに変更している例が挙げられている