🍣

単体テストの考え方/使い方

に公開

理解できたところだけまとめてみる

単体テスト概要

単体テストの意義

単体テストを行うプロジェクトとそうでないプロジェクトでは持続可能性が変わってくる。
初期はどちらも成長していくが、時間が経つにつれテストをしないプロジェクトは成長が鈍化していく。

単体テストの定義

  • 単体(unit)と呼ばれる少量のコードを検証する
  • 実行時間が短い
  • 隔離された状態で実行される
    隔離された状態というのはロンドン学派と古典学派によって解釈が異なる
    ロンドン学派:協力者オブジェクトを隔離(テスト対象のクラスが他のクラスに依存するのであればその依存の全てをテスト・ダブルに置き換えなくてはならない)
    古典学派:テストケース同士の隔離(前のテストの振る舞いが後のテストの振る舞いに影響してはならない)

コード網羅率と分岐網羅率

コード網羅率 = 総行数 / 実行されたコードの行数
分岐網羅率 = 経由された経路の数 / 分岐経路総数の内

コード網羅率の問題点

三項演算子のような形で書いていると片方の条件にしか実行されないとしても100%になり得る

分岐網羅率の問題点

実際にテスト対象のコードが検証されたかを保証できない(結果を確認する必要がある)
使用するライブラリ内のコードは計測の対象から外れる

共通の問題点

テストの質を測る場合、コード網羅率 > 分岐網羅率ではあるが上記の通り問題がある。
カバレッジが低いことでテストの質が低いことは確認できるが、カバレッジが高いからといって質が高いということを言うことはできない

単体テストでの名前の付け方

最も役に立たない命名規則
{テスト対象メソッド}{事前条件}{想定する結果}

fun Sum_TwoNumbers_ReturnsSum()

fun Sum_of_two_numbers()

テストメソッドに名前をつけるときの指針

  • 厳格な命名規則に縛られないようにする
  • 問題領域に精通している非開発者に対してどのような検証をするのかが伝わるような名前をつける
  • 英語の場合はアンダースコアで単語を区切る

    // 悪い例
    fun IsDeliveryValid_InvalidDate_ReturnsFalse()
    // 良い例
    fun Delivery_with_a_past_date_is_invalid()
    どういう振る舞いをするのかと言うのに注力して命名する

パラメータ化テストへのリファクタリング

検証する振る舞いが非常に複雑な場合、その振る舞いに対するテストケースは劇的に多くなってしまう。
なのでライブラリなどを用いてテストケースを1つにまとめるパラメータ化テストを導入するべき。

良い単体テストの4本柱

  • 退行に対する保護
    • 新機能を追加した時既存の機能でバグらないか
  • リファクタリングへの耐性
    • テストが失敗することなくどれくらいプロダクションコードのリファクタリングを行えるか
  • 迅速なフィードバック
  • 保守のしやすさ
    • テストケースを理解するのにどれくらい難しいか
    • テストを行うことがどれくらい難しいのか
      コードは資産ではなく負債と言う意識が大事

モックとスタブの違い

  • テスト・ダブル: プロダクションコードには含まれずテストでしか使用されない偽りの依存として表現されるすべてのもの
    • モック:テスト対象システムからその依存に向かって行われる外部に向かうコミュニケーション(出力)を模倣する
      • モック
      • スパイ
    • スタブ:テスト対象システムに向かって行われる内部に向かうコミュニケーション(入力)を模倣する
      • スタブ
      • ダミー
      • フェイク

単体テストの3つの手法

  • 出力値ベーステスト
    • テスト対象のコードに入力値を渡したあと、そこから返される結果を検証する
  • 状態ベーステスト
    • 検証する処理の実行が終わった後にテスト対象に状態を検証する
  • コミュニケーションベーステスト
    • モックを用いて協力者オブジェクトとの間で行われるコミュニケーションを検証する

この中で出力値ベーステストが最も費用対効果の高いテスト。
実装の詳細と結びつかず、リファクタリング体制を維持する労力は少なく保守しやすいため。

状態ベーステストとコミュニケーションベーステストは実装の詳細と結びつき、記述するコードも多くなるのでコストが高い

→ すべてにおいて出力値ベーステストを用いるように努めるべき

単体テストのアンチパターン

  • プライベートな状態の公開
    • テストのためにプライベートなメソッドを公開してはいけない
    • テストが難しい場合適切なカプセル化ができていない可能性がある
  • テストへのドメイン知識の漏洩
    • テストコードに実装の詳細コードのコピペがあるのは問題
  • プロダクションコードへの汚染
    • 例)テストの時はログを出力しないようにするためにプロダクションコードにテストかどうかのフラグを設定する

Discussion