modules/npm/packages/ に新しい npm/pnpm パッケージを追加するスキル
test-design-discipline
テストの追加・修正・削除、または実装後の検証方針を判断するときに使う。タスクごとに反射的な回帰テストを増やさず、守るべき仕様、不安な仕様、実際に使われる経路、事前条件と事後条件を中心にテストを設計するためのスキル。
含まれるファイル(1)
- SKILL.md11.1 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
テスト設計の規律
基本方針
テストは「実装したから書く」ものではなく、「壊れてほしくない仕様を固定する」ために書く。
依頼された挙動を毎回そのまま回帰テストとして追加しない。まず、そのテストが将来どの誤変更を防ぐのか、既存テストや型で既に守られていないかを確認する。
タスクが正しく行われたかの確認と、将来の仕様を守るテストは別物である。確認は実行、出力や生成物の確認、一時的なスクリプトで行ってよい。テストスイートに残すのは、テスト追加の判断を通ったものだけにする。
テスト対象は内部実装ではなく、利用者から見える振る舞い、ドメインルール、不変条件、境界処理、外部システムとの接点にする。テストでは事前条件と事後条件を表し、途中でどの関数が何回呼ばれたかを知りすぎないようにする。
仕様を守る手段の順序
仕様を守る手段には順序がある。上の手段で守れた仕様は、下の手段で重ねて守らない。
- 型で消す: 不正な状態や誤った呼び出しを、型の上で作れないようにする(domain-design-safety-discipline を参照)。型で消せた仕様にテストは不要である
- 所有する層で 1 回テストする: 型で消せないルールは、そのルールを所有する最もドメイン寄りの層でテストする
- 外側の層は固有の責務だけをテストする: usecase や presentation では、その層が新たに足す責務(配線、変換、副作用の順序)だけを対象にする。内側で守った仕様を重ねて確認しない
テストの維持コストは、ドメインに近いほど低く、presentation に近いほど高い。書く時点の手間はどの層でもほとんど変わらないため、書きやすさを層選びの根拠にしない。同じ仕様を守れるなら、最も内側の層で書く。同じ仕様を複数の層でテストするのは、各層で守る仕様が異なると言い切れる場合に限る。
テスト追加の判断
テストを書く前に、その仕様を型で消せないかを先に検討する(「仕様を守る手段の順序」を参照)。型で消せるなら、テストではなく設計で守る。
優先してテストを書く場面:
- 絶対に守るべき仕様がある
- 変更が不安な仕様や壊れやすい境界に触れる
- issue、障害、レビュー指摘、ユーザー報告などで具体的に示された不具合を再現し、再発を防ぎたい
- ドメインルール、不変条件、可視性、操作可能性に関わる
- DB、HTTP、シリアライズ、時刻、並行処理、外部 API などの境界に関わる
- 新しく導入した再利用可能な helper や純粋関数が、複数箇所の判断を支える
テストを足さない判断をしてよい場面:
- 型や constructor によって不正状態が作れない、または今回の変更でそう設計し直せる
- 既存テストが同じ仕様を実際の経路で十分に押さえている
- getter、単純な mapper、明白な分岐など、テストしても仕様の保護にならない
- 実装詳細を固定するだけで、将来の改善を不必要に縛る
- 「バグかもしれない」「仕様かもしれない」という推測だけで、守るべき挙動を特定できていない
- 今回のタスクの確認には有効だが、長期的な回帰テストとして残す価値が薄い
テストを追加する場合は、そのテストが将来どの誤変更を防ぐのか説明できるようにする。
タスクの写しテストを見分ける
次のシグナルに当てはまるテストは、タスクの確認をテストの形で残そうとしている可能性が高い。書く前に、テスト追加の判断へ戻る。
- 期待値が、依頼内容をそのまま assert に書き写したものになっている
- 期待値が、同じ差分の中で自分が追加した実装(エラー文言、マッピング、フィールドの対応)の転写になっている
- 生成関数や constructor の結果について、フィールドが入力と一致することだけを assert している
- codegen の出力、スキーマ、設定ファイルなど、他の正本から機械的に導かれる生成物の内容を assert している。その内容は型や生成器が既に保証していないか
- このテストが落ちるのは誰かが意図的にこの変更を戻したときだけで、事故で壊れる経路が存在しない
「利用者から見える」「外部との境界に関わる」に見えても、その内容が型や生成器から機械的に導かれるなら、まず型側の保証で足りないかを確認する。なお、公開 API の後方互換のためにスキーマ全体を契約として検査する仕組み(スキーマ diff の CI チェックなど)は仕様の保護であり、これはフィールドごとの存在 assert をタスクごとに積むこととは別物である。
エラーレスポンスのマッピングも同じ区別で扱う。status code や errorCode が外部との契約なら、守る手段はマッピング表全体を 1 箇所で検査する契約テストやスキーマ検査であり、タスクごとに個別のエラーを assert することではない。契約テストでも、対象は安定した識別子(status code、errorCode)に限定し、説明文の文言は assert しない。
タスクの確認が必要なら、テストを残す代わりに一時的な手段で検証し、報告では「どう検証したか」「テストとして残すもの」「残さない理由」を分けて述べる。
実際の経路でテストする
守りたい仕様がある場合は、できるだけ実際に使われる経路に沿ってテストする。
- public API、usecase、handler、command、query など、利用者が通る入口から確認する
- 内部 helper だけを直接テストして、実際の経路が壊れている状態を見逃さない
- ただし、純粋関数やドメインルールのように独立した意味を持つ処理は、単体でテストしてよい
- 広すぎる回帰テストをやみくもに増やさず、守る仕様に対して必要な経路を選ぶ
「入口から確認する」と「最も内側の層で書く」は矛盾しない。ドメイン層でテストした仕様について、実際の経路がその層を必ず通ることを型が保証しているなら、入口からの重複テストは不要である。型が保証していない配線は、外側の層の固有の責務としてテストする。
実装詳細を知らないテスト
テストは、原則として事前条件と事後条件に集中する。
避けるもの:
- private method や内部 helper の呼び出し順に依存するテスト
- mock に「この関数がこの順番で何回呼ばれ、そのたびにこれを返す」と細かく教えるテスト
- 内部データ構造、分割方法、loop の回数などを固定するテスト
- 実装の都合で変わる snapshot やログ出力への過剰な依存
望ましいもの:
- 入力、事前状態、実行、観測可能な結果を明確にする
- 永続化結果、返り値、公開イベント、外部境界への意味ある出力を確認する
- mock より fake、in-memory 実装、test double、実際の軽量 adapter を優先する
- 外部サービスに依存する場合も、プロトコルや payload の意味を確認し、呼び出し回数だけを目的にしない
Mock の扱い
mock は外部境界を切り離すための道具であり、内部実装を固定するための道具ではない。
- mock を使う前に、fake や純粋関数への切り出しで確認できないか考える
- mock の期待は、仕様上意味のある外部出力に限定する
- 「何回呼ばれたか」は、それ自体が仕様である場合だけ確認する
- テストが mock の準備で読みにくくなるなら、設計や境界が複雑すぎないか見直す
差分レビュー
テストを書いた後、または書かない判断をした後に行う。差分と守るべき仕様だけを渡された第三者でも同じ結果になるように、対象を列挙してから、対象ごとの問いに答える。誰がこのレビューを行うかは、このスキルでは決めない。実装の進め方を管理する側に従う。
手順:
- 対象を列挙する: 追加・変更したテスト / 削除したテスト / テストを書かない判断をした仕様。
- 対象ごとに下の問いに答える。「問題なし」で済ませず、根拠を 1 行で言い切る。言い切れない対象は、本文の該当節へ戻ってテスト設計から見直す。
- 問題が見つかった対象は修正し、修正した対象にもう一度この手順を適用する。
追加・変更したテストごとの問い:
- 仕様: このテストはどの仕様を守り、将来どの誤変更を防ぐか
- 型: この仕様は型で消せないか。消せるなら、テストではなく設計で守る
- 層: この仕様を守れる最も内側の層で書かれているか。外側の層で書いたなら、その層固有の責務(配線、変換、副作用の順序)を対象にしているか。同じ仕様を別の層のテストが既に守っていないか
- 経路: 実際に使われる入口(public API、usecase、handler など)を通っているか。単体でテストしているなら、独立した意味を持つ純粋関数やドメインルールか
- 読み取り: 入力、事前状態、実行、観測可能な結果がテストから読み取れるか
- 実装詳細: 呼び出し順、mock の戻り値設定、内部データ構造、snapshot に依存していないか。依存しているなら、それ自体が仕様か
- 重複: 型、constructor、既存テストで既に保証されている性質を繰り返していないか。広すぎる回帰テストで将来の改善を縛っていないか
- 写し: 期待値が今回の依頼内容や差分の写しになっていないか。タスクの確認と仕様の固定を区別したか
削除したテストごとの問い:
- そのテストが守っていた仕様は、何によって引き続き守られるか
テストを書かない判断ごとの問い:
- その仕様は何で担保されるか(型 / 既存テスト / ビルド / 静的検査)を言い切れるか。推測だけで守るべき挙動を特定できていないなら、「テスト追加の判断」へ戻る
レビュー
まだレビューはありません。使ってみた感想をお寄せください。
同じリポジトリのスキル
概要と使いどころ
install/ 配下に新しいセットアップ用シェルスクリプトを追加するスキル
GitHub issue を新規作成・下書きするとき、または既存 issue の本文を書き直すときに必ず使うスキル。「issue を作って」「issue 化して」「これを issue にまとめて」のような依頼で発火し、`gh issue create` を直接呼ぶ前に必ず参照する。人間が合意した内容だけを、意味を足さずに issue へ残すための基準を定める。
GitHub に Pull Request を作成するスキル。ユーザーが「PRを作って」「プルリクエストを出して」「PR作成して」と言ったとき、またはコミット済みの変更をレビューに出したいときに使用する。
ドメインモデル、可視性、操作可能性、不変条件、層境界、API 設計に関わる実装時に使う。不正な状態や誤った呼び出し方を、型、関数の境界、ドメイン語彙で表し、正しい使い方が自然になる設計へ寄せるためのスキル。
コミットやPR作成の前に、作業ブランチが適切かどうかを確認・切り替えるスキル。git-commit や create-pr からサブルーチンとして呼び出される。