はじめに
本記事は、私が最近読んだ著書 「単体テストの考え方/使い方」で紹介されている、リファクタリングに強いテストを書くためにはどうするべきか、という記述について簡単にまとめたものです。ここで紹介する内容以外にも多くの素晴らしい情報が詰まった本なので、是非皆様も読んでみてください!
リファクタリングへの耐性
まず、本書において作者は、テストの目的とはバグを起こすことなく、新しい機能の追加やリファクタリングを行えるようにすることだと述べています。 そこで、テストコードが備えるべき要件の一つとして、リファクタリングへの耐性を挙げています。
参考:良い単体テストを構成する4本の柱
- バグに対する保護
- リファクタリングへの耐性
- 迅速なフィードバック
- 保守のしやすさ
リファクタリングへの耐性とは、テストが失敗することなく、コードのリファクタリングが行えるかどうかを示す指標です。 例えば、リファクタリングを行った結果、テスト対象の機能的な振る舞いは変わっていないのに関わらず、テストが失敗するようになった場合、そのテストコードは耐性が低いということになります。
偽陽性
このように、テストコードが意図通りに動いているのに関わらず、テストが失敗することを「偽陽性」と言います。 リファクタリング耐性の高いコードを作成するためには、この偽陽性に対して注意を払わなければなりません。例を挙げると、メソッドの引数を増やしたことによってテストがコンパイルエラーを起こすようになった、というのも偽陽性に含まれます。
偽陽性が増すと、テストコードの信頼性が損なわれていきます。 正しく振る舞っているはずなのに、テストが失敗するケースが増えると、リファクタリング時に発生したバグが無視されるようになったり、あるいはリファクタリング自体を敬遠するようになってしまいます。
対策
テストコードから偽陽性を取り除くためには、テスト対象の内部的な実装から切り離す必要があります。 つまり、コードを呼び出す側にとって意味ある実行結果のみを確認し、内部でどのような処理が行われるかは検証しないようにします。
設計が正しくなされている場合、これはほぼパブリックメソッドと一致します。 逆に言えば、プライベートメソッドの検証は一切すべきではないということです。プライベートメソッドを検証してしまうと、テストが実装の詳細と結びついてしまい、壊れやすいテストとなってしまうからです。
もし、パブリックメソッドからの検証が困難であるほどプライベートメソッドの実装が複雑になっている場合、プライベートメソッドの一部をクラスに切り出すなど、何かしらの設計変更が必要かもしれません。
まとめ
リファクタリング耐性の高いテストコードを書くためには、実装の詳細(プライベートメソッド)を検証せず、パブリックメソッドを通じて、利用側の期待する振る舞いのみを検証するようにしましょう。