mockable-code について
外部依存をテストダブルに置き換えられるようコードを構成し、本番設計を歪めずにテスト可能性を高めるコーディングスキルです。依存境界、テストダブルの選び方、レビュー方法を扱います。
主な機能
- 01方針と副作用の分離
- 02境界での協力オブジェクトの明示
- 03テストダブルの選択ガイド
- 04モック可能性のレビュー基準とスキャナー
活用シーン
- 01ネットワークやストレージに依存するコードの追加
- 02外部作用が隠れたコードのリファクタリング
- 03脆い相互作用前提のテストのレビュー
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jpcaparas/ skills --skill mockable-codeインストール後、Claude Code で/mockable-codeを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdmetadata.jsonREADME.mdskill-card.prompt.mdagents/.gitkeepassets/.gitkeepevals/evals.jsonreferences/boundaries.mdgotchas.mdprinciples.mdreview-rubric.mdsource-notes.mdtest-doubles.mdscripts/analyze_mockability.pytest_skill.pyvalidate.pytemplates/mockability-review.md詳細
mockable-code
本番設計をゆがめることなく、依存関係をモック、スタブ、フェイク、またはインメモリ実装に置き換えられるように、コードの作成、編集、リファクタリング、レビューを行います。
パッシブトリガー
タスクがソースコードに関係し、動作が協調オブジェクト、I/O、時刻、乱数、永続化、設定、ネットワーク呼び出し、キュー、SDK、プロセス状態、またはフレームワークのコンテキストに依存する場合は、バックグラウンドでこのスキルを読み込みます。小さな編集では軽量に適用してください。基本ルールを黙って適用し、最終的な実装に影響するモック可能性の判断だけを伝えます。
判断ツリー
何をしていますか?
-
依存関係を呼び出すコードを追加する場合: 実用的であれば、呼び出し元から依存関係を受け取ります。ドメインポリシーを、構築、設定、トランスポートから分離します。
-
テストしにくいコードをリファクタリングする場合:
references/boundaries.mdを読みます。動作を変更する前に、副作用を必要最小限の境界の背後に移します。 -
テストダブルを選ぶ場合:
references/test-doubles.mdを読みます。動作を証明できる、最も能力の低いダブルを優先します。定型的な応答にはスタブ、現実的なインメモリ動作にはフェイク、やり取りの契約にはモックまたはスパイを使います。 -
コードをレビューする場合:
references/review-rubric.mdを使います。隠れた副作用、ハードコードされた協調オブジェクト、壊れやすいやり取りのテスト、契約テストの不足を優先して指摘します。 -
別のエージェントやチームメイト向けに作業を計画する場合: 依存関係の境界、使用するダブル、検証コマンド、停止条件を含めます。依存関係と担当者を明示せずに「注入可能にする」といった曖昧な助言は避けます。
-
ユーザーが最大限の速度や使い捨てのプロトタイプを求める場合: 本番コードはシンプルに保ちますが、コストが低い場合は高コストまたは取り消し不能な作用を分離します。将来のテストで使うかもしれないというだけで、広範な抽象化を構築しないでください。
クイックリファレンス
| 状況 | デフォルトの対応 |
|---|---|
| ネットワーク、データベース、ファイル、時刻、乱数、環境変数、またはキューに直接アクセスする | 外側の境界に留めるか、小さな契約を介して渡します |
| ビジネスルールと SDK 呼び出しが混在している | ルールを決定的な関数に切り出し、SDK を呼び出すアダプターを注入します |
| コンストラクターがクライアントを内部で生成する | ローカルのパターンで別の方法が定められていない限り、クライアント、ファクトリー、または設定を呼び出し元から受け取ります |
| 静的またはグローバルなシングルトン依存 | 境界で明示的な協調オブジェクト、コンテキストオブジェクト、または限定的なラッパーを優先します |
| フレームワークのハンドラー | 解析とレスポンスの整形はハンドラーに残し、ポリシーを呼び出し可能なサービスや関数に移します |
| テストで実際の外部サービスが必要 | 共有インフラストラクチャに接続する前に、フェイク、スタブ、契約テスト、またはローカルのテストコンテナーを使います |
| インターフェースが有用に思える | 実際に置き換えが必要な場合、保護すべき境界がある場合、または言語の慣習で必要な場合に限って追加します |
| レビューでモックできないコードが見つかった | 一律の書き換えではなく、隠れた依存関係を明示し、最小限の修正方法を示します |
基本ルール
- 判断と作用を分離します。ドメインポリシーは、実際のネットワーク、時計、ファイル、データベース、キュー、乱数を使わずにテストできるようにします。
- モジュール、コンストラクター、関数、ハンドラー、またはコンテキストの境界で、協調オブジェクトを明示します。
- 依存関係ができることすべてではなく、呼び出し元が必要とする機能を表す、限定的な契約を優先します。
- 本番環境でのデフォルトの配線は簡便に保ちつつ、テストではグローバル状態をモンキーパッチせずに協調オブジェクトを差し替えられるようにします。
- 状態を伴う動作にはフェイク、固定の応答にはスタブ、重要なやり取りにはモックやスパイ、アダプターには契約テストを使います。
- テストからアクセスさせるためだけに、非公開の内部要素を公開しないでください。動作を、より適切な名前を持つ単位に移します。
- 想定上の代替物しかない抽象化は追加しないでください。使われないインターフェースを重ねるより、明確な置き換え箇所が1つあるシンプルなコードを優先します。
- 新しいテストパターンを導入する前に、ローカルのスタイル、フレームワークの慣習、既存のテスト基盤を尊重します。
モック化可能性の確認ゲート
コード変更を完了する前に、このゲートを頭の中で確認し、利用できる場合はローカルのツールでも確認してください。
| ゲート | 合格条件 |
|---|---|
| 依存関係の所有者 | 各外部依存関係を誰が作成し、誰が利用するのかを読者が把握できます |
| 置き換え可能性 | テストで、遅い、不安定、コストがかかる、または取り消せない協力オブジェクトを、本番コードを変更せずに置き換えられます |
| 決定性 | 時刻、乱数、プロセスや環境の読み取りが境界で制御されています |
| 契約の規模 | インターフェースやプロトコルには、呼び出し側が実際に必要とする操作だけが含まれています |
| 本番環境での配線 | 通常の実行経路が分かりやすく、追跡しやすい状態です |
| テストの意図 | テストでは、偶発的な呼び出し順序ではなく、振る舞いや重要な契約を検証しています |
| 失敗時の挙動 | 依存関係からのエラー経路を、実際の副作用を起こさずにシミュレートできます |
| スコープ | 無関係な書き換えやフレームワークの不要な変更を伴わずに、変更によってテスト容易性が向上します |
作業ワークフロー
-
まず周辺を調査します。 変更を設計する前に、周辺の構築方法、依存性注入、テストフィクスチャ、モック、アダプターのパターンを確認します。
-
固定された依存関係を特定します。 実際の I/O、時刻、乱数、設定、SDK、データベース、フレームワーク、グローバル状態へのアクセスを列挙します。どれを境界に留めるべきか判断します。
-
最小の置き換えポイントを選びます。 コードベースで通常使われている仕組みを利用します。コンストラクターパラメーター、関数パラメーター、インターフェース/プロトコル、ファクトリー、コンテキストオブジェクト、モジュールインポートのオーバーライド、フィクスチャ、フレームワークプロバイダーなどです。
-
テスト専用の無理な変更をせずに実装します。 本番コードの名前は実態に即したものにします。テストだけを目的として、セッター、公開された可変フィールド、広範なサービスロケーターを追加しないでください。
-
意味のあるテスト用代替物で検証します。 依存関係を置き換え、失敗時の経路を網羅する、対象を絞ったテストを追加します。また、境界が変化しうる箇所では、少なくとも1つのアダプターまたは統合テストを維持します。
-
明確に報告します。 変更した依存関係の境界、使用したテスト用代替物やフィクスチャ、実行したコマンド、残っている実サービスのリスクを記載します。
省略可能なヘルパー
ヘルパーは、判定ではなく、すばやくレビューするためのプロンプトスキャナーとして使用してください。
python3 scripts/analyze_mockability.py /path/to/project
python3 scripts/analyze_mockability.py /path/to/project --json
ハードコードされた副作用やグローバル変数の可能性がある箇所に印を付け、人間やエージェントが確認できるようにします。スキャンで何も検出されなくても、コードがモック化可能であるとは限りません。多く検出されても、コードに問題があるとは限りません。
読み進め方
| 必要な情報 | 参照先 |
|---|---|
| デフォルト設定の背後にある原則 | references/principles.md |
| 依存関係と副作用の分離 | references/boundaries.md |
| モック、スタブ、フェイク、スパイ、契約テストの選択 | references/test-doubles.md |
| レビュー結果と重大度の順序 | references/review-rubric.md |
| よくある落とし穴とアンチパターン | references/gotchas.md |
| 参照元の影響と適用に関する注記 | references/source-notes.md |
落とし穴
- モック化可能なコードと、モックを多用するテストは同じではありません。脆さを軽減できる場合は、振る舞いテストやフェイクを優先してください。
- すべてのクラスにインターフェースを用意すると、コードが理解しにくくなります。置き換えや所有者の境界が実際に存在する場合に、契約を追加してください。
- クロック、乱数生成器、環境変数、グローバルコンテキストからの隠れた読み取りは、ネットワーク呼び出しと同様にテストを壊すことがあります。
- モンキーパッチは役立つことがありますが、本番コードの中核で協力オブジェクトを置き換える唯一の方法にしてはいけません。
- 本番環境での配線に統合テストや契約テストによるカバレッジがない場合、依存性注入によって構築時のエラーが隠れることがあります。
- テストのためにプライベートメソッドを公開しないでください。実際のポリシーユニットを切り出すか、観測可能な振る舞いを通じてテストしてください。