本文へスキップ

Skills

chimera-prove-the-test-discriminates

失敗するところを一度も見ていないリグレッションテストは、推測にすぎません。commit する前にコードを壊し直し、赤くなるのを確認してください。

パターン来歴: cleanステータス: activev0.1.0 · Apache-2.0

カードを読んだレビュアーが与えるものであり、ファイルが自分について主張するものではありません。信頼できない内容を扱った実行中にエージェントが抽出したカードは汚染された状態で生まれ、取得される前にレビュー待ちとして保留されます。

どんなときに思い出すか

  • 回帰テストを書いた
  • バグを直してテストを足した
  • テストが通ったから出す
  • 修正の後でテストを書く

価値はたいてい「回避」と「確認」にあります。「実施」は誰でも書ける節です。

上のカード本文は翻訳です。CLI が取り込み、エージェントが実行時に読み、下のハッシュが証明するのは英語の原文です。

トリガー

バグを直し、それが再発しないようにテストを書いた。そのテストは通る。それが手元にある証拠のすべてであり、そしてそれは、そのテストがバグについて何も検証していなかった場合に得られる証拠とまったく同じものだ。

これはテストが修正のに書かれた場合に最も強く当てはまる。その場合、そのテストが実行された唯一のコードは、すでにバグが取り除かれたコードだからだ。TDD ループの中で先に書かれ、赤くなったテストにはあまり当てはまらない——そちらはすでに失敗するところを見ており、それこそがループの全目的である。

「before」に戻せる状態が存在しない、純粋に新しい挙動のためのテストには当てはまらない。そこでの弁別的な確認は別物だ。新しい実装を削除し、バグではなくその不在によってテストが失敗することを確認すること。

実施

  1. 修正を構成する正確な編集を特定する。コミットではなく、hunk だ。修正が比較演算子1つの変更なら、その比較こそがこれから元に戻すものである。
  2. それを元に戻す。修正を git stash するか、その1行を手作業で壊れていた形に戻す。
  3. 新しいテストだけを実行する: pytest tests/test_thing.py::test_the_regression -x
  4. 失敗を読む。それは自分のアサーションが失敗しているものであり、その挙動についてのメッセージでなければならない——SyntaxError でも、収集エラーでも、中途半端に戻したファイルからの ImportError でもない。それらは誤った理由での赤であり、そのテストについて何も証明しない。
  5. 修正を戻し(git stash pop)、同じテストが今度は通ることを確認する。
  6. その事実をテストの docstring に書く。何を元に戻したか、そしてその失敗がどう見えたかだ。次にこのテストに触れる人は、それがバグに対して一度でも実行されたことを知る必要がある。

回避

どちらの世界でも真であるものを検証すること。典型的な形はこうだ:


result = parse_config(raw)
assert result is not None
assert "timeout" in result

バグは、timeout が int の 30 ではなく文字列の "30" として返ってきていたことだった。どちらのアサーションも、いずれの場合でも成立する。弁別するのは、変わったものの方だ:

# RIGHT — fails against the buggy code
assert parse_config(raw)["timeout"] == 30
assert isinstance(parse_config(raw)["timeout"], int)

また、「手っ取り早く失敗させる」ために関数まるごとをコメントアウトしたり import を削除したりして元に戻すことも避けること。それはそのファイル内のどのテストでも起きたはずの赤い実行を生むだけで、このテストについては何も教えてくれない。元に戻す対象は修正であり、修正だけでなければならない。

そして、テストが本番では決して使われない経路を通って修正済みのコードに到達する、という惜しい間違いも避けること——それは別種の失敗であり、そのためのカードは chimera-test-the-wiring-not-the-class である。

確認

二者択一の問いが1つ: 修正前のコードに対して、このテストが自身のアサーションによって失敗を出力するところを、自分の目で見たか。

見たなら、それで終わりだ。答えが「失敗したはずだ」であるなら、何も確認していない——その一文は、そのテストを書いたのと同じ推論が生み出した予測にすぎない。

機械的には:

git stash                 # remove the fix
pytest tests/test_x.py::test_regression   # MUST be red, on your assertion
git stash pop             # restore
pytest tests/test_x.py::test_regression   # MUST be green

赤のち緑、その両方を観測したか、さもなければそのテストは未証明である。

リスク

元に戻すのが安くない修正もある。依存関係のアップグレード、スキーマのマイグレーション、削除されたファイル、ベンダリングされたライブラリ内の変更などだ。そこで巻き戻しを強行すると、テストの価値以上のコストがかかりうる。誠実な代替手段は、以前は壊していた入力を再構成して特定の値を検証し、赤い実行は観測していないと docstring にはっきり書くことだ。未証明であることを認めている未証明のテストは、確認されたかのように匂わせるテストよりはるかにましである。

このカードの本当の危険は、元に戻したまま戻し忘れることだ。汚れた作業ツリーでこの手順を踏むと、再び壊された行が、自身のリグレッションテストと並んでコミットされることになる——修正を別のファイルで戻してしまったためにスイートは緑のまま、という形で。コミット前に毎回 git diff を実行すること。

そして弁別的なテストも、自分が理解したバグの範囲でしか良くならない。それが証明するのは、そのテストがこの巻き戻しを捕捉するということだ。症状ではなく根本原因を直したことは証明しないし、赤のち緑をその証拠として扱うのは、証拠が支えられる以上の主張である。

使い方

カードはデータです。リポジトリをクローンし、パスで取り込んでください。ネットワーク経由で届いたものは汚染扱いとなり、承認されるまで保留されます。それが望ましい挙動であり、ここにワンライナーのインストーラーがない理由です。

git clone https://github.com/brcampidelli/chimera-agent.git
chimera skills-import chimera-agent/skills/chimera-prove-the-test-discriminates/SKILL.md

完全性

公開された状態のファイルの SHA-256。取り込む側は、受け取ったものがこのページに表示されたものと同じか確認できます。

90d4bf00eb67055008527ee372153b2637fa2feea601ed6760997eda239f0b76

リポジトリでカードを読む