単体テストのお話


はじめに

前回、ここでテスト自動化の投稿をしたので、
続きとして書いてますが、おそらく単一投稿として読めるかと思います。
なお、TDD界隈の単体テストの話ではなく、普通の品質保証の話なのでご注意ください。
基本的にJUnit入門をとても参考にしてます。

1. 単体テストとは

  • クラスやメソッドを対象としたプログラムを検証するためのテスト
  • ソフトウェアテストにおける最も粒度の小さいテストレベル

2. 進め方

こんな感じで進めるのがよいのかなと。
1. テストの実装対象を決める
2. ホワイトボックステストの技法など駆使してパターンを網羅する
3. テスト実装する
4. レビュー

実際にレビューまで出来るかはプロジェクトの忙しさ次第ですが、
テストの網羅性やアサートのロジック(何を以ってテストOKとするか)を
見たい人も一定数いるのではなかろうかと思います。

3. 対象とすべき箇所・分量

とても厳しいカバレッジ基準が設けられているプロジェクトは特に悩まなくていいかもですが、
そうでない場合は、ユニットテストに限らずよく悩むかなと。
テスト自動化のピラミッド的には分厚くしないといけないテストレベルではありますが、
実際のところ、時間的余裕がない場合もあるので、その時はROIやリスクの観点から判断が必要です。
ただし、個別にやる/やらないを判断する時間があるなら、その時間でテスト実装したほうがいいので、
もう少しざっくりとした判断基準として考えられるのは以下のようなものです。

やったほうがいい

  • コメントが必要な程度に複雑なロジック
    • If文がネストしている(モノによってはリファクタリングが必要な気がしますが)
    • 複雑な計算ロジック・オブジェクトを扱う
  • 多くのオブジェクトから依存関係のあるロジック

やらなくていい

  • データの受け渡しのためのモデルクラス(e.g. Java Bean)
  • MVCモデルにおけるViewの部分等、人の目で見たほうがいいもの
  • その他、非常に手間のかかる割に効果が薄い箇所

4. 単体テスト実装のポイント

不安定なテストを避ける

不安定なテストが混じっていると、まずはそのテストへの関心が薄れます。
そして、本当のテストNGへの関心が薄れてしまうという、狼少年問題が発生します。

この原因の1つとしてはテスト対象が現在時刻に依存している場合等が考えられます。
その場合は、Mock/Stubを使うなどして安定させると良いでしょう。
もう1つはテストないしテスト対象が実行環境に依存している場合が考えられます。
例えばファイルパスがハードコーディングされている場合等です。
プロダクトコード及びテストの安定稼働のためには、
特定の実行環境に依存しない実装を心がけましょう。

問題の局所化

一つのテストケースに多くのテスト条件やassertを入れてしまうと、
テストNGの時に疑うべき箇所が増えてしまい、問題の特定に時間がかかります。
そのため、なるべくシンプルなテスト条件・assertにする方が好ましいです。

独立したテスト

テストケースの実行順序に依存しないテストが好ましいです。
こうしておくと後々、テストを並列実行させやすくなります。

不明瞭なテストを避ける

ここは「テスト」を「コード」という単語に置き換えてもらえれば理解いただけるかと思いますが、
テストも可読性が大事だということです。具体的にはテストの前提条件、テストの手順、
assertの処理が明確に分かれた、短いテストコードが好ましいです。
よく練られたテストケースは、テスト対象のコードの使い方を説明する
ドキュメントとしても機能するため、他の人が読むことを意識して書くと良いでしょう。

5.アサートするもの

  • テスト対象メソッドの返り値
  • テスト対象クラスの内部状態
  • テスト対象が依存する特定メソッドの呼び出し回数
  • テスト対象が依存する特定メソッドを呼び出した時の引数

6. まとめ

自動テストはやはり繰り返し実行することが前提です。
そのため、長く付き合うことを考えると単体テスト実装のポイントは
押さえたほうが良いなと思います。

なお、ここで書いたのは基本的な話なので、こまい話はこちらをどうぞです。

参考

JUnit入門