コンポーネント凝集の原則 (テストモジュールのまとめ方)
プロダクションコードの構造とテストコードの構造を一致させることで、コードの変更時にどのテストを修正すべきかを見つけやすくするためです。
CCP: 閉鎖性共通の原則 (Common Closure Principle)
概要
同じ理由、同じタイミングで変更されるテストは、同じファイルにまとめるべきです。
メカニズム
プロダクションコードの変更時に、修正が必要なテストコードの範囲を最小限に抑えます。
あっちこっちの取っ散らかった場所に、テストコードが存在すると、リファクタリングが自信をもってできない要因にもなりえます。
具体例
src/services/payment_service.pyのテストは、
すべてtests/services/test_payment_service.pyに配置します。
支払いロジックの変更で影響を受けるテストは、このファイルに限定されます。
REP: 再利用・リリース等価の原則 (Reuse/Release Equivalence Principle)
概要
複数のプロジェクトで再利用されるテストコンポーネント(例: テスト用JWT生成ライブラリ)は、独立したパッケージとしてバージョン管理され、リリースされるべきです。
メカニズム
複数のプロジェクトやチームで共有されるテスト用のヘルパーライブラリに安定性をもたらします。
具体例
認証機能のテストで使うJWTトークン生成ヘルパーなど、複数のマイクロサービスで共通して使われるテスト支援コードは、独立したcompany-auth-test-helpersのようなライブラリとしてパッケージ化し、v1.2.0のようにバージョンを付けて配布します。
CRP: 全再利用の原則 (Common Reuse Principle)
概要
テストの利用者は、自身が利用しないテストヘルパーに依存すべきではありません。
なので、テストコンポーネント内の一部のみ使うといった設計は、この原則に反します。
メカニズム
巨大で多機能なテストユーティリティモジュールを避け、小さく、目的に特化したモジュールに分割することを促します。
また、関連性の低いテストを同じファイルに混在させるべきではありません。
ユーザー認証のテストと商品検索のテストは、関心事が全く異なるため、別のファイルに分けるべきです。
具体例
一つのtest_utils.pyにDB操作、APIクライアント、データ生成など全てのヘルパーを入れるのではなく、db_helpers.py、api_client.pyのようにファイルを分割します。
DB操作のテストだけを書きたい開発者は、db_helpers.pyだけをインポートすれば済みます。
コンポーネント結合の原則 (テストモジュール間の依存関係)
こっちは、仕様が変わった際に、上記で「どのテストを修正すべきか」を発見し、
そこを変えることで、どこのテストに影響するのか?をコントロールしやすくします。
ADP: 非循環依存関係の原則 (Acyclic Dependencies Principle)
概要
テストモジュール間の依存関係に循環があってはなりません。
影響の伝搬が無限ループしかねませんからね。
相互依存し合った、テストモジュールがあったら要注意です。
メカニズム
循環依存は、テストスイートのロード失敗や、理解不能な依存関係を引き起こします。
具体例
テストヘルパーAがヘルパーBをインポートし、ヘルパーBがヘルパーAをインポートするような構造は許されません。
依存の方向を一方通行にするか、共通のヘルパーCに機能を切り出す必要があります。
SDP: 安定依存の原則 (Stable Dependencies Principle)
概要
依存の方向は、より安定したコンポーネントに向かうべきです。
メカニズム
頻繁に変更される不安定なテストコードが、めったに変更されない安定したテスト基盤に依存するように設計します。
具体例
個別のマイクロサービスAのテストコード(不安定)は、全社共通のテスト基盤ライブラリ(安定)に依存すべきです。
逆に、安定したテスト基盤が、特定のサービスAのテストコードに依存するようなことがあってはなりません。
SAP: 安定度・抽象度等価の原則 (Stable Abstractions Principle)
概要
コンポーネントの抽象度は、その安定度と極力比例すべきです。
横軸が不安程度 I、縦軸が抽象度 Aである。
メカニズム
安定したコンポーネントは抽象的に、不安定なコンポーネントは具体的になるようにテストコードを設計します。
具体例
安定・抽象
全社共通のテスト基盤ライブラリは、特定のサービス名などを知らず、インターフェースや設定によって振る舞いが決まる抽象的な作りであるべきです。
不安定・具体的
特定のアプリケーション機能のテストコードは、その機能で使う具体的なAPIエンドポイント名やテストデータなどをハードコードした具体的な作りになります。
