徒然なるままに

学習メモがメインです

「単体テストの考え方/使い方」で学んだこと

この本を読んだので学んだこと記載します。
単体テストの考え方/使い方 | マイナビブックス

読んだ理由

この本は去年の12月年末くらいにtwitterで流れてきて知りました。単体テストがどのような役割を果たせるのか改めて学べそう、そして、効果的な単体テストを学べそうと思ったので読んでみました。

感想

分厚い本ですごく読み応えがありました。読むのが少し大変でしたが、その分学びになったことが多かったです!
古典学派とロンドン学派の考え方、初めて知りましたが面白かったです。この本は古典学派推しで、そして個人的にも古典学派の方が好みなので*1、この本を好きになれたのだと思います!
そして、「良いテストの4本柱」はすごく良いなと思いました。これを意識して4本柱は単体テスト、統合テスト、システムテスト、それぞれのレベルを考える時に役立ちそうだと思いました。また、リファクタリングへの耐性を得るためにテストケースをブラックボックスで作成することについても、すごく納得できました。

カバレッジについて、体温の例は分かりやすかったです。そして、「テストの質がいかに優れているか自動的に評価できる方法はない」とのことだったので、定量的に測る方法が存在してほしかったと少し思いました…。できないことを知れたということでプラスに捉えます...!

学びになったこと(抜粋)

テストケースの質

  • カバレッジが低い=テストの質が悪い。そして、カバレッジが高い≠テストの質が良いとなる。
  • カバレッジが高いことは良いことだが、そのことを強制することは開発の妨げになる。
    • 例)病院での体温計測
      • 患者の体温を計測した際に、計測した結果の体温が高ければ、発熱していることがわかる。もし体温を下げることだけを目標としている場合、クーラーを使って冷たい風を浴びせ続けることで体温を下げれる。しかしこれは治療ではない
  • テストの質がいかに優れているか自動的に評価できる方法はない。
  • 優れたテストスイートの特長
    • テストすることが開発サイクルの中に組み込まれている
    • コードベースの特に重要な部分のみがテスト対象となっている
    • 最小限の保守コストで最大限の価値を生み出すようになっている
      • 価値があるテストケースを認識できること
      • 価値があるテストケースを作成できること

単体テストにおける古典学派とロンドン学派の違い

単体テストの定義(古典学派)

  1. 1単位の振る舞い (a unit of behavior) を検証すること
  2. 実行時間が短いこと
  3. 他のテストケースから隔離された状態で実行されること
    • テストケースを隔離する=各テストケースをお互いに影響を与えることなく個別実行できるようにする

単体テストの定義(ロンドン学派)

  1. 1単位のコードを検証すること
  2. 実行時間が短いこと
  3. 協力者オブジェクトから隔離された状態で実行されること
    • テスト対象が他のクラスに依存しているならば、不変依存を除くすべての依存をモックに変える必要がある
隔離対象 単体の意味 テストダブルの置き換え対象 聖典
古典学派 テストケース 1つのクラス、もしくは、同じ目的を達成するためのクラスの1つのグループ(1単位の振る舞い) 共有依存 テスト駆動開発
ロンドン学派 単体 1つのクラス 不変依存を除くすべての依存
※ロンドン学派=モック主義者とも言われる理由
実践テスト駆動開発

良いテストを構成する4本の柱

  1. 退行 (regression) に対する保護
    • テストによって実行されるコードが多くなるほど、多くのバグを見つけることができる性質のこと
  2. リファクタリングへの耐性
    • いかに偽陽性を生み出すことなく、プロダクションコードに対してリファクタリングを行なえるかを示す性質のこと
    • ほとんどの場合、リファクタリングの耐性を備えれるか備えられないかしかできない(All or Nothing)
    • 偽陽性とは
      • 嘘の警告のこと。機能に影響がないのにテストが失敗すること。
      • 偽陽性よる影響
        • 嘘の警告に慣れてしまい、その警告に注意を払わなくなってしまうことで、プロダクションコードに潜む問題を解決しようとする意思を弱めてしまう
        • テストへの信頼が失われる
      • 発生する理由
        • テストケースがテスト対象の内部的なコードと結びつくことで発生してしまう。実装の詳細(例:手順が正しいか?)をテストしているから。回避するためには、テスト対象の実行結果を検証すべき。
  3. 迅速なフィードバック
    • テスト実行時間がどのくらい短くなるかに影響する性質のこと
  4. 保守のしやすさ
    • 何をテストしているかを理解することがどれくらい難しいのか??→テストケースのサイズが小さいほど、可読性が高い
    • テストを実施することがどれくらい難しいのか??→テスト時のプロセス外依存が少ないほど、テスト実施が簡単

  • テストケースの価値は、構成する4本柱がどのくらい備わっているか掛け算で求められる。
    • テストケースの価値 = [0..1] * [0..1] * [0..1] * [0..1]
      • テストケースが価値を持つためには、4本の柱を全て備えなくてはならない
    • これらは解析ツールなので正確な数値を算出できるものではない。
  • 4本柱全てを最大限に備えることは不可能
    • 退行 (regression) に対する保護、リファクタリングへの耐性、迅速なフィードバックは互いに排反するから。
      • 3つの柱を全て最大限にできないのはCAP定理と似ている
    • 保守のしやすさだけ独立している。
    • リファクタリングへの耐性を排除することはできない。備えるか否かの選択しかできないから。
    • 3本柱はテストレベルごとで調整をおこなう。トレードオフ
      • E2Eでは退行に対する保護が重要になり、単体テストは迅速なフィードバックの方がより重要になる

ブラックボックステストホワイトボックステスト

4種類のプロダクションコード

  • ドメインモデル/アルゴリズム
    • 単体テストに対する費用対効果が最も高い
      • 協力者オブジェクトが含まれないから保守コストが低い
  • 取るに足らないコード
    • テストする価値が全くない
  • コントローラ
    • 統合テストでテストされるべき
  • 過度に複雑なコード
  • コードの複雑さ=分岐の数
  • ドメインにおける重要性=コードがプロジェクトの問題領域においてどのくらい重要なのか
    • コードが複雑になればなるほど、ドメインにおける重要性は高くなる傾向
    • コードが単純になれば、ドメインにおける重要性は低くなる傾向
      4種類のプロダクションコード

統合テストについて

以下の1つでも定義から外れると統合テストとなる。

1. 1単位の振る舞い (a unit of behavior) を検証すること 
2. 実行時間が短いこと
3. 他のテストケースから隔離された状態で実行されること
    - **テストケースを隔離する=各テストケースをお互いに影響を与えることなく個別実行できるようにする**
  • 一般的に統合テストでは、1件の最長のハッピーパス、及び、単体テストでは扱えなかった異常ケースをできるだけ多く扱う。単体テストでは、ビジネスシナリオにおける異常ケースを可能な限り多く扱う。
    • 全てのプロセス外依存とのやりとりを検証できるような、長いハッピーパスを見つけ出す。そのようなパスが見つからないのであれば、テストケースを増やしていき、全ての外部システムとのやりとりが検証されるようにする。
  • 4本柱との関係性
    • 統合テストは、単体テストより優れた「退行に対する保護」と「リファクタリングへの耐性」を得ることができる。(単体テストよりも優れていないといけない)
    • 統合テストは「保守のしやすさ」と「迅速なフィードバック」の要素を失う。
    • これらのテストの種類が違うことで生じるトレードオフを表現したものがテストピラミッド。

疑問点

この本で言っている統合テストは、JSTQBでいうところのシステム統合な?それともコンポーネント統合?

*1:テスト対象メソッドやテスト対象クラスの振る舞いを確認していました。振る舞いを確認できない時に、必要最低限でモックと捉えていました。