t0mmy

2020年12月30日に参加

学習履歴詳細

単体テストの考え方・使い方 5章 モックの利用とテストの壊れやすさ 読了

やったこと

  • 単体テストの考え方・使い方 5章 モックの利用とテストの壊れやすさ 読了

学んだこと

学び

  • モックとスタブは考え方としては別物
  • スタブとのやり取りは、決して検証してはならない
    • テストが実装に依存するようになり、リファクタリング時に壊れやすいテストとなってしまうため
  • コマンドクエリ原則の考え方
  • 「観測可能な振る舞い」か「実装の詳細」か、に加えて、「キチンと設計されたコード」の考え方

気付き

  • モックとスタブは、目的を考えるとどちらも同じもの
    • 目的 : テスト対象のテストをやりやすくする
    • 手段 : 外部依存をテスト用オブジェクトに置き換える
  • スタブとのやり取りは、決して検証してはならない

    • テストすべきは、「外部依存からデータを取得して、アレコレ処理を行う」という、メソッドが行う一連の振る舞い
  • public/private の考え方は、Design by Contract にも通ずる者があるように思う

    • 公開しているメソッド(API)はクライアントと強く結びつくため、変更が困難
    • => 変更が困難なため、公開メソッドをテストするコードは、リファクタリングで壊れにくい
    • 反対に、非公開メソッドは、開発者の一存でコロコロ変わる可能性が高い
    • => 容易に変更できてしまうため、非公開メソッドをテストするコードは、リファクタリングで壊れやすい
  • 単一責任の原則に従ってコードを書くと、自然と「キチンと設計されたコード」になる気がする。

    • 「最も提供したい機能1つ」を提供するメソッドのみpublicへ
    • 上記を補佐するメソッドは全てprivateへ
  • 必要十分な「観測可能なふるまい」とならない原因は、設定ミスと設計ミスの二パターンが考えられる

    • 設定ミス : private やパッケージプライベートとすべきところをpublic に設定してしまっている
    • 設計ミス : private にしたくてもできないような状態

メモ

  • モックとスタブは、考え方としては別物
    • モックは、外向きの通信の模倣・検証を指す
    • 特に、外部依存の状態を変更するような処理(HTTP POSTなど や SQLの Insert Update Delete など)
    • スタブは、内向きの通信の模倣を指す
    • 特に、外部依存からデータを取得するような処理(HTTP GET や SQLのselectクエリなど)
    • テストツールが提供するmock 機能を使用して、モック処理やスタブ処理を実装する
  • スタブとのやり取りは、決して検証してはならない
    • スタブとのやり取りは、実装の一部分
    • テストが実装に依存するようになり、リファクタリング時に壊れやすいテストとなってしまうため
  • コマンドクエリ原則
    • メソッドは、コマンドかクエリのどちらかだけを満たすようにするべき(両方を満たすメソッドは極力作成しない)
    • コマンド: 副作用有り、戻り値なし => モック対象
    • クエリ : 副作用無し、戻り値あり => スタブ対象
  • プロダクションコードを、「観測可能なふるまい」と「実装の詳細」の二つに分類して考える
    • 「観測可能なふるまい」がテスト対象
    • 「観測可能なふるまい」とは、以下のどちらかを満たすもの
    • クライアントの目標達成に直接貢献する、公開された操作
    • クライアントの目標達成に直接貢献する、公開された状態
    • ここで言う「クライアント」とは、文脈によって変化する
      • 同じコードベース上にあるコードだったり、外部アプリケーションだったり、UIだったり
    • 例)
    • setter , getter , コンストラクタ等は「観測可能な振る舞い」
    • setterやコンストラクタ内で実行するvalidationをまとめたprivateメソッドは「実装の詳細」
    • キチンと設計されたコードとは、観測可能な振る舞いが、公開されたAPIと一致しており、実装の詳細が、プライベートなAPIとして隠されている
    • 例)
      • パッケージを超えて使用したいメソッドを、観測可能な振る舞いとして public に設定
      • パッケージ内にのみ公開したいメソッドをパッケージプライベートに設定
      • クラス内でのみ使用したいメソッドを、実装の詳細として private に設定
    • 一つの操作で、クライアントの目標を達成できる状態が良い
      • 目標達成のために複数のpublicメソッドを叩く必要がある場合は、実装の詳細が漏洩している可能性を疑う
    • 適切なカプセル化によって、実装の詳細を隠蔽する
    • カプセル化は、「コードから、dataの整合性を破綻させる不変条件の侵害を守る」手段
  • ヘキサゴナルアーキテクチャ
    • ドメインロジックを含む「ドメイン層」と、ドメイン層と外部アプリケーションを繋ぐ「アプリケーション・サービス層」で構成
    • アプリケーション・サービス層 => ドメイン層 という、一方向の依存関係
      • 別の見方をすると、アプリケーション・サービス層は、取り換え可能である必要がある
  • 「実装の詳細」を判断する、別の見方
    • システム内通信は、実装の詳細
    • システム間通信は、実装の詳細ではない
    • システム間通信だと、public メソッドの契約(例:後方互換性)を考える必要がある
    • これを検証するためにテストを実施する ... と考えると良いかも?
    • システム間通信の検証で活躍するのが"モック"

■この章で扱う事

  • モックとスタブの違い
  • 観察可能なふるまいと実装の詳細の定義
  • モックの利用とテストの壊れやすさとの関係
  • リファクタリングへの耐性を損なわずにモックを使う方法

■まとめ

  • テストダブル 「ダミー、スタブ、スパイ、モック、フェイク」の総称
    • モックグループとスタブグループに大別できる
    • モックグループは外部に向かう通信(出力)の模倣・検証に使用する
      • 外部APIの状態を変更する処理など
    • スタブグループは内部に向かう通信(入力)の模倣に使用する
      • 外部APIからデータを取得する処理など
  • テストツールが提供するmock機能を使用することで、モックグループやスタブグループの処理を実装できる
    • モックグループやスタブグループを明確に区別しているテストツールは存在しなさそう
  • スタブとのやり取り部分の検証は、壊れやすいテストにつながる
    • 例)
      • 詳細なSQL文のテストは、テーブル定義が変わると破綻する
      • HTTP Request のヘッダ・ボディの詳細な検証は、REST提供側の定義が変わると破綻する
  • コマンド・クエリ分離
    • すべてのメソッドは、コマンドかクエリのどちらかの性質のみを満たすべき、という提唱
      • 言い換えると、両方を満たすべきではない
    • コマンドの置き換えがモック、クエリの置き換えがスタブに相当
  • クライアントに公開された操作や状態は「観察可能な振る舞い」であり、非公開の操作や状態は「実装の詳細」
    • ここで言う「クライアント」は、文脈によって変化する 同じコードベース上にあるコードだったり、外部アプリケーションだったり、UIだったり
  • 「キチンと設計された」コードとは、観察可能な振る舞いが、公開されたAPIと一致しており、実装の詳細がプライベートなAPIとして隠されるコードのこと
  • 「カプセル化」とは、不変条件の侵害からコードを守るための手段
  • ヘキサゴナルアーキテクチャ
    • 性質
      • ドメイン(ビジネスロジック)層とアプリケーション・サービス(ドメイン層と外部アプリケーションの連携)層同士の関心が分離されている
      • アプリケーション・サービス層 => ドメイン層 という、一方向の依存関係となっている
        • 言い換えると、アプリケーション・サービスは付け替え可能でなければならない
      • アプリケーション・サービス層はI/Fとして機能する
        • ドメイン層を動かす場合は、必ずアプリケーション・サービス層を経由することになる
    • ヘキサゴナルアーキテクチャでは、各層は、観察可能な振る舞いのみ公開するようにする
  • システム内コミュニケーション : アプリケーション内のクラス間で行う通信のことであり、実装の詳細
  • システム間コミュニケーション : 外部アプリケーションと行う通信のことであり、観察可能なふるまいの一部となる
  • システム内コミュニケーション の確認にモックを使用すると、テストが壊れやすくなる
  • 以下を両方満たす場合、モックを使用できる
    • システム間コミュニケーション
    • 外部から副作用を観察できる場合
テスト

2023年03月25日(土)

3.0時間