assert-what-the-generator-found
クラッシュするジェネレーターは失敗したと教えてくれますが、もっともらしい出力を返すものは何も教えてくれません。実行されたことではなく、何を見つけたかをテストしてください。
カードを読んだレビュアーが与えるものであり、ファイルが自分について主張するものではありません。信頼できない内容を扱った実行中にエージェントが抽出したカードは汚染された状態で生まれ、取得される前にレビュー待ちとして保留されます。
どんなときに思い出すか
- コードジェネレータを書いた
- スキーマダンプをテスト中
- 出力は正しそうに見える
- ライブラリから構造を抽出する
価値はたいてい「回避」と「確認」にあります。「実施」は誰でも書ける節です。
上のカード本文は翻訳です。CLI が取り込み、エージェントが実行時に読み、下のハッシュが証明するのは英語の原文です。
トリガー
ある表現を読み込み、別の表現を出力するものを書いた場合に当てはまる。スキーマダンプ、リファレンス抽出器、マイグレーション、スクレイパー、インデックスビルダーなど、出力量が多すぎて誰も全体を読まないもの全般が対象だ。
目視で確認できる単一の値を返す関数には当てはまらない。ここでのリスクは、「見た目は正しそうだ」がその出力に対して行われる唯一のレビューになってしまうほど出力が大きい場合に固有のものだ。
実施
- テストを書く前に、件数を明確にする。コマンド、行、ファイル、フィールドが何個出力されるべきか。その数字はジェネレーター以外の場所——ソース、ドキュメント、手作業のカウント——から得ること。
- その件数、またはその下限を検証する。
assert resultではなくassert len(groups) >= 10とする。 - 存在するはずだと分かっている、具体的で名前のある項目の存在を検証する。3〜4個で十分であり、異なる形の入力から選ぶこと。
- ジェネレーターが分類を行うなら、各クラスが空でないことを検証する。すべてを一つのバケツに入れてしまう分類器の不具合は、これで捕捉できる。
回避
assert build() ——これはジェネレーターが空のリスト、一部だけのリスト、あるいは項目ごとに微妙に間違った種類が混ざったリストを返しても通ってしまう。
サードパーティの構造をたどる際に isinstance を信頼するのも避けること。ライブラリは依存関係を自前にバンドルすることがある。TyperGroup はファイルがインポートした click.Group のインスタンスではない、なぜなら Typer は Click の独自コピーを同梱しているからだ。オブジェクトが名乗っているクラスが何かではなく、必要なもの——commands マッピング、items メソッド——を持っているかを問うこと。duck-typing はバンドルされた依存関係やメジャーバージョンアップを乗り越えるが、クラスチェックはどちらも乗り越えられず、例外を出すのではなく誤分類という形で失敗する。
確認
意図的にジェネレーターを壊し、テストが失敗するのを確認する。サブコマンドへの再帰処理をコメントアウトするか、型チェックがすべてを拒否するようにして、スイートを実行する。
それでもスイートが通ってしまうなら、そのテストは「ジェネレーターが実行された」ことしか検証していない——このスキルが防ごうとしているまさにそのテストを書いてしまったということだ。
リスク
件数を厳密に固定すると、テストは保守の手間になる。正当に追加されたコマンドのたびに赤くなってしまうからだ。下限(>= 10)と名前付き項目の組み合わせを優先し、厳密な件数は、決定なしには本当に変わってはならないものにだけ取っておくこと。
また限界もある。この種のアサーションは、ジェネレーターがカテゴリ丸ごと落としたことは捕捉するが、ある項目の1つのフィールドだけが間違っているケースは捕捉しない。そうではないふりをすることも、それ自体が一種の過信である。
使い方
カードはデータです。リポジトリをクローンし、パスで取り込んでください。ネットワーク経由で届いたものは汚染扱いとなり、承認されるまで保留されます。それが望ましい挙動であり、ここにワンライナーのインストーラーがない理由です。
git clone https://github.com/brcampidelli/chimera-agent.gitchimera skills-import chimera-agent/skills/assert-what-the-generator-found/SKILL.md完全性
公開された状態のファイルの SHA-256。取り込む側は、受け取ったものがこのページに表示されたものと同じか確認できます。
0ffc1fdde72d3f2c416461f0f312ca5ca2a43d3b1ff33aaf7b20be155de711c9