Claude Media
Claude CodeでRSpecを回し、失敗だけを絞って直させる

Claude CodeでRSpecを回し、失敗だけを絞って直させる

Claude CodeにRSpecを回させるとき、CLAUDE.mdへ書くテストコマンドと、--fail-fast・--only-failures・--next-failureで失敗だけを再実行する反復、権限ルールの書き方をまとめます。

Claude CodeにRSpecを回させると、失敗の出力がそのまま会話に流れ込みます。スイート全体を毎回走らせれば、無関係な出力でコンテキストが埋まります。時間もかかります。

RSpecには、失敗した例だけを再実行する--only-failures、最初の失敗で止める--fail-fast、その両方を束ねた--next-failureがあります。これをCLAUDE.mdに書いておけば、Claudeは「全件で現状を知る、失敗だけで直す、最後に全件で確かめる」という反復を自分で回せます。

失敗を絞るオプションは何が違うのか

まず、RSpecが持つ絞り込みの手段を並べます。

目的コマンド動き
最初の失敗で止めるコマンドrspec --fail-fast動き最初の失敗でスイートを止める
N件の失敗で止めるコマンドrspec --fail-fast=3動き3件失敗した時点で止める
前回失敗した例だけコマンドrspec --only-failures動き前回の失敗だけを実行する
失敗を1件ずつ潰すコマンドrspec --next-failure動き--only-failures --fail-fast --order definedの省略形
1つのファイル・行コマンドrspec spec/a_spec.rb:37動き行番号に該当する例かグループを実行する
名前で絞るコマンドrspec -e "文言"動き例の説明文に一致するものを実行する

--fail-fastを外したいときは--no-fail-fastを渡します。これが既定の挙動です。

--only-failuresだけは準備が要ります。RSpecが「前回どの例が失敗したか」をファイルに書き残すので、その保存先を先に決めなければなりません。

失敗の記録先を設定する

--only-failuresと--next-failureは、config.example_status_persistence_file_pathに指定したファイルを読みます。設定がないまま実行すると、次のエラーで止まります。

To use `--only-failures`, you must first set `config.example_status_persistence_file_path`.

spec/spec_helper.rbに次のように書きます。パスはRSpecの例にならったexamples.txtです。

RSpec.configure do |c|
  c.example_status_persistence_file_path = "examples.txt"
end

このファイルは実行のたびに書き換わる作業用の記録です。コミットする意味はないので、.gitignoreに入れておくと、Claudeがgit add -Aで拾う事故を避けられます。ここはRSpecの規定ではなく、運用上の選択です。

オプションの既定値は.rspecにまとめられます。RSpecは次の順でオプションファイルを読み、後ろのものが前のものを上書きします。

  • グローバル: $XDG_CONFIG_HOME/rspec/options、なければ~/.rspec
  • プロジェクト: リポジトリ直下の./.rspec
  • ローカル: ./.rspec-local(gitignoreに入れてよい個人用)

コマンドラインの指定とSPEC_OPTS環境変数は、さらに優先されます。チームで共有したい設定は.rspec、自分の端末だけで変えたい設定は.rspec-localに置く、という分け方ができます。

CLAUDE.mdに書くテストコマンド

Claudeは、CLAUDE.mdを強制される設定ではなく文脈として読みます。そのため、確かめられる程度に具体的な指示が効きます。たとえば「テストを走らせる」ではなく「npm testを走らせる」と書く、という考え方です。RSpecなら、コマンドそのものと使い分けの条件を書きます。

# テスト(RSpec)
- 全件: `bundle exec rspec`
- 失敗だけ再実行: `bundle exec rspec --only-failures`
- 失敗を1件ずつ: `bundle exec rspec --next-failure`
- 1ファイル: `bundle exec rspec spec/models/user_spec.rb`
- 修正の途中は失敗した例だけを回し、
  作業を終える前に必ず全件を1回通す
- 失敗の記録は examples.txt。手で消さない・コミットしない

最後の2行がこの書き方の肝です。絞り込みは「途中経過を速く見る」手段であり、完了の判定ではありません。--only-failuresは前回失敗した例しか走らせないので、直した変更が別の例を壊していても気づけません。完了条件を全件の成功に固定しておけば、その穴が閉じます。

CLAUDE.mdの長さにも気を配ります。memoryのページは、CLAUDE.mdを1ファイル200行以内に収めることを目安としています。RSpecの運用が長くなる場合は、spec/配下の作業だけに効く指示を.claude/rules/へ切り出す手があります。CLAUDE.md全体の書き分けはCLAUDE.mdの実践パターンにまとめています。

権限ルールをbundle execに合わせる

テストのたびに許可を聞かれると、反復が止まります。許可ルールは次のように書けます。

{
  "permissions": {
    "allow": [
      "Bash(bundle exec rspec *)"
    ]
  }
}

末尾の *は前方一致で、bundle exec rspec単体にも一致します。空白を入れずにBash(bundle exec rspec*)と書くと、別名のコマンドまで広く拾うので、空白は残します。

ここで落とし穴があります。Claude Codeがルール照合の前に取り除くラッパーは、timeout・time・nice・nohup・stdbufなどの固定リストです。bundle execはその中にありません。つまりBash(rspec *)と書いても、Claudeがbundle exec rspecと打てば一致しません。ルールはClaudeが実際に打つ形に合わせます。

もう1つ、cdや&&でつないだ複合コマンドは、各サブコマンドが別々にルールと照合されます。bundle exec rspecだけを許可していても、bundle install && bundle exec rspecのbundle install側は別に許可が要ります。詳しくはBashの許可ルールと複合コマンドで扱っています。許可の棚卸しは許可プロンプトを減らす方法が参考になります。

失敗を絞って直す反復の流れ

Claudeに頼むときの流れは次の4段です。

手順

RSpecの失敗を潰す4段

  1. 1

    全件で現状を知る

    bundle exec rspecを1回回し、失敗の一覧を記録ファイルに残します。

  2. 2

    失敗だけを回して直す

    --only-failuresで失敗した例だけを再実行し、原因を直します。

  3. 3

    1件ずつ潰す

    失敗が多いときは--next-failureで、最初の1件に集中します。

  4. 4

    最後に全件を通す

    絞り込みを外して全件を回し、他の例を壊していないか確かめます。

Claudeへの指示は、たとえば次のように短く書けます。

spec/models の失敗を直して。まず bundle exec rspec で現状を把握し、
その後は --next-failure で1件ずつ直して。最後に全件を通して報告して。

ベストプラクティスのページも、Claudeに実行できる確認手段(テストスイート、ビルドの終了コード、リンタなど)を渡すと、人が張り付かなくても進められると述べています。RSpecはまさにその確認手段です。

--next-failureの動き

--next-failureは、失敗を1件に絞って直し、次の1件に進むための近道です。RSpecの例では、失敗が3件あるとき、最初の実行で走るのは1件です。その1件を直して再実行すると、直った例と次の失敗の2件が走り、失敗は1件のまま残ります。

これは仕様どおりの動きです。直った例も一度は走り、記録が更新されるので、次の失敗が順に表に出てきます。失敗がすべて消えたあとにもう一度実行すると、All examples were filtered outと表示されます。この表示が出たら、残る失敗がないというサインです。そこで全件の実行に戻します。

--next-failureは--order definedを含むため、ランダム順の設定(--order random)があっても、このときは定義順で走ります。失敗の順序が実行ごとに入れ替わらないので、Claudeが同じ失敗を追い続けられます。

他の絞り込みを使い分ける

失敗の記録に頼らず、場所や名前で絞る手もあります。

ファイル名に行番号を付けると、その行にある例や、行を含むグループだけが走ります。

bundle exec rspec spec/models/user_spec.rb:37

例の説明文で絞るなら-e(--example)です。説明文は、describeの入れ子とitの文言をつないだ全文と照合されます。複数回渡せば、複数の条件を指定できます。

bundle exec rspec -e "User" -e "validation"

タグでの絞り込みは、--tag focus(-t focus)のように書きます。focus: trueを付けた例だけが走ります。~slowのようにチルダを付けると除外です。ただし、同じキーに複数の値を指定しても、最後の1つしか効きません。~type:aと~type:bを並べて両方除外する書き方は、期待どおりに動きません。

どれを使うかは、手元に何があるかで決まります。

  • 失敗が出たばかりで、記録ファイルが新しい: --only-failuresか--next-failure
  • 直したい例の場所が分かっている: ファイル名と行番号
  • 名前だけ分かる: -e
  • 以前から付けた分類がある: --tag

Claudeには、まず記録ファイルを使う方法を勧めておくのが扱いやすい構成です。場所や名前の指定は、Claudeが失敗メッセージの行番号を見て自分で組み立てられます。

順序依存の失敗は--seedと--bisectで追う

ランダム順で走らせていると、特定の例を先に実行したときだけ落ちる失敗が出ます。ある例がクラスのメソッドを書き換えてしまい、後から走る例が影響を受ける、といった型です。--only-failuresで失敗した例だけを回すと、その「先に走る例」が含まれず、失敗が再現しないことがあります。

RSpecの機能説明には、--order randomと--seedが、他の例を先に実行したときだけ失敗する不安定な例を表に出す、とあります。同じ順序をもう一度走らせるには、--seedに数値を渡します。

そこで、失敗の原因が順序にありそうなときは--bisectを足します。--seedなどのオプションと一緒に渡すと、RSpecがスイートの一部を繰り返し走らせ、同じ失敗を再現する最小の例の組を探します。

bundle exec rspec --seed 1234 --bisect

RSpecの例では、10件のうち1件が失敗するスイートを4回の絞り込みで調べ、次のような再現コマンドを出しています。

rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] --seed 1234

途中でctrl-cを押しても、その時点で見つかっている最小の再現コマンドが表示されます。詳しい経過が要るときは--bisect=verboseです。ここで得た再現コマンドをClaudeに渡せば、2件だけを回して原因を直せます。

注意点が1つあります。--next-failureは--order definedを含むので、定義順で走ります。ランダム順のときにだけ起きる順序依存の失敗は、このオプションでは隠れます。--next-failureで通ったのに全件で落ちるときは、--seedと--bisectに切り替えます。CLAUDE.mdにも「順序が怪しいときは--seedを付けて--bisect」と一行足しておくと、Claudeが同じ手順で動けます。

つまずきやすい点

--only-failuresは失敗のあるファイルしか読まない

RSpecの例では、全件でpassing_spec.rbが読まれる一方、--only-failuresでは読まれませんでした。読み込み時の副作用に依存するファイルがあると、絞り込みの実行と全件の実行で挙動が変わることがあります。絞り込みで通っても、最後に全件を通す理由の1つです。

ファイルや行の指定と組み合わせられる

--only-failuresはディレクトリやファイル名と組み合わせられ、読み込んだ例の中から失敗だけが走ります。rspec spec/models --only-failuresで、モデルのspecの失敗だけに絞れます。

説明文のない例は-eで絞れない

it { is_expected.to be > 8 }のようなワンライナー形式は、実行して初めて説明文が決まるため、-eでは直接選べません。その場合はグループの説明文で絞ります。

記録ファイルが古いと前の実行の失敗を再現する

記録は前回の実行結果なので、コードが大きく変わったあとは実態とずれている可能性があります。迷ったら全件を1回回して、記録を更新します。

Gitフックと組み合わせる

テストの実行を、コミット前のフックへ寄せる構成もあります。フックが失敗したときの直させ方は、huskyとpre-commitの併用に書いてあります。RSpecをフックに載せるなら、フックには全件、日常の反復には--only-failures、と役割を分けると、速さと確実さを両立できます。

Railsアプリを土台から作る流れはRailsアプリ開発の手順が扱っています。そこで作ったアプリに、この記事の運用を足すかたちになります。

まとめ

RSpecの絞り込みは、道具として十分にそろっています。難しいのはオプションを知ることより、どこまでを絞り込みに任せ、どこからを全件に戻すかの線引きです。CLAUDE.mdに「途中は--only-failuresか--next-failure、終わる前に全件」と書き、許可ルールをBash(bundle exec rspec *)にそろえれば、Claudeは失敗を潰す反復を人の手を借りずに回せます。ランダム順で起きる失敗だけは、記録ファイルでは追えません。そのときは--seedで順序を固定し、--bisectで原因の組み合わせまで縮めます。記録ファイルの設定を忘れると--only-failuresが動かない点も、最初に押さえておきます。

この記事を共有:XはてブLinkedIn