
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時間