mcpField
T
test-driven-development

test-driven-development

github.com/lucianghindaテスト・品質44066 回インストール1 日前に更新
リポジトリ

test-driven-development について

test-driven-development は、コーディングエージェントにテスト駆動開発の手順を示すスキルです。機能追加、バグ修正、リファクタリングなどで、失敗するテストを先に確認してから最小限のコードを実装します。

主な機能

  1. 01Red-Green-Refactor のサイクルに従う
  2. 02実装前に失敗するテストを作成・確認
  3. 03テストを通す最小限のコードを実装
  4. 04テスト通過後にリファクタリング
  5. 05チェックリストとアンチパターンを提示

活用シーン

  1. 01新機能をテスト先行で実装
  2. 02失敗するテストでバグを再現して修正
  3. 03リファクタリング時に動作の退行を確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/lucianghinda/superpowers-ruby --skill test-driven-development

インストール後、Claude Code で/test-driven-developmentを入力して読み込まれたか確認します。

同梱ファイルtesting-anti-patterns.mdtesting-strategy.md

詳細

テスト駆動開発(TDD)

概要

まずテストを書きます。失敗することを確認します。テストを通すための最小限のコードを書きます。

基本原則: テストが失敗するのを確認していないなら、そのテストが正しいことを検証しているかどうかわかりません。

ルールの文面に違反することは、ルールの精神に違反することです。

使用するタイミング

常に:

  • 新機能
  • バグ修正
  • リファクタリング
  • 振る舞いの変更

例外(人間のパートナーに相談してください):

  • 使い捨てのプロトタイプ
  • 生成されたコード
  • 設定ファイル

「今回だけTDDを省略しよう」と考えましたか? やめてください。それは自己正当化です。

絶対のルール

NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST

テストより先にコードを書きましたか? 削除してください。最初からやり直します。

例外はありません:

  • 「参考用」として残さない
  • テストを書きながら「調整」しない
  • 見ない
  • 削除とは、削除することです

テストをもとに、まっさらな状態から実装します。それだけです。

レッド・グリーン・リファクタリング

digraph tdd_cycle {
    rankdir=LR;
    red [label="RED\nWrite failing test", shape=box, style=filled, fillcolor="#ffcccc"];
    verify_red [label="Verify fails\ncorrectly", shape=diamond];
    green [label="GREEN\nMinimal code", shape=box, style=filled, fillcolor="#ccffcc"];
    verify_green [label="Verify passes\nAll green", shape=diamond];
    refactor [label="REFACTOR\nClean up", shape=box, style=filled, fillcolor="#ccccff"];
    next [label="Next", shape=ellipse];

    red -> verify_red;
    verify_red -> green [label="yes"];
    verify_red -> red [label="wrong\nfailure"];
    green -> verify_green;
    verify_green -> refactor [label="yes"];
    verify_green -> green [label="no"];
    refactor -> verify_green [label="stay\ngreen"];
    verify_green -> next;
    next -> red;
}
RED - 失敗するテストを書く

期待する動作を示す、最小限のテストを1つ書きます。

```ruby test "retries failed operations 3 times" do attempts = 0 operation = -> { attempts += 1 raise "fail" if attempts < 3 "success" }

result = retry_operation(operation)

assert_equal "success", result assert_equal 3, attempts end

明確な名前、実際の動作をテストし、対象は1つ
</Good>

<Bad>
```ruby
test "retry works" do
  call_count = 0
  stub_op = -> { call_count += 1 }
  retry_operation(stub_op)
  assert_equal 3, call_count  # tests call count, not behavior
end

曖昧な名前、結果ではなく呼び出しをテストしている

要件:

  • 動作は1つ
  • 明確な名前
  • 実際のコード(避けられない場合を除き、モックは使わない)
REDを確認 - 失敗するのを確認する

必須です。絶対に省略しないでください。

bin/rails test test/models/retry_test.rb

確認事項:

  • テストが失敗する(エラーではない)
  • 失敗メッセージが期待どおりである
  • 機能が未実装のために失敗している(タイプミスが原因ではない)

テストが成功する場合: 既存の動作をテストしています。テストを修正してください。

テストでエラーが発生する場合: エラーを修正し、正しく失敗するまで再実行してください。

GREEN - 最小限のコードを書く

テストを通すための最も簡単なコードを書きます。

```ruby def retry_operation(fn, max_attempts: 3) attempts = 0 begin fn.call rescue => e attempts += 1 retry if attempts < max_attempts raise end end ``` 通すために必要な分だけ ```ruby def retry_operation(fn, max_attempts: 3, backoff: :linear, on_retry: nil, jitter: false, timeout: nil) # YAGNI end ``` 過剰な設計

機能を追加したり、ほかのコードをリファクタリングしたり、テストの範囲を超えて「改善」したりしないでください。

GREENを確認 - 成功するのを確認する

必須です。

bin/rails test test/models/retry_test.rb

確認事項:

  • テストが成功する
  • ほかのテストも引き続き成功する
  • 出力がクリーンである(エラーや警告がない)

テストが失敗する場合: テストではなく、コードを修正してください。

ほかのテストが失敗する場合: すぐに修正してください。

REFACTOR - 整理する

グリーンになった後にのみ、次のことを行います:

  • 重複を取り除く
  • 名前を改善する
  • ヘルパーを抽出する

テストを成功した状態に保ちます。振る舞いは追加しないでください。

繰り返す

次の機能に対する、次の失敗するテストを書きます。

良いテスト

品質良い例悪い例
最小限対象は1つ。名前に「and」があるなら、分割してください。test('validates email and domain and whitespace')
明確名前が動作を説明しているtest('test1')
意図がわかる期待するAPIを示しているコードが何をすべきかをわかりにくくしている

なぜ順序が重要なのか

「動作することは後でテストして確認すればいい」

コードの後に書かれたテストは、即座に通ります。即座に通ることは、何も証明しません:

  • 間違ったものをテストしているかもしれない
  • 振る舞いではなく、実装をテストしているかもしれない
  • 忘れていたエッジケースを見逃すかもしれない
  • そのテストがバグを検出するところを一度も見ていない

テストファーストでは、テストが失敗するところを必ず確認することになり、そのテストが実際に何かをテストしていることが証明されます。

「エッジケースはすべて手動でテスト済みだ」

手動テストは場当たり的です。すべてをテストしたと思っていても:

  • どのようなテストを行ったかの記録がない
  • コードが変更されたときに再実行できない
  • プレッシャーの下では、ケースを忘れやすい
  • 「試したときは動いた」は、網羅的であることを意味しない

自動テストは体系的です。毎回同じ方法で実行されます。

「X時間の作業を削除するのは無駄だ」

サンクコストの誤謬です。その時間はすでに失われています。今あなたが選べるのは:

  • 削除して、TDD で書き直す(さらにX時間、信頼度は高い)
  • そのまま残して、後からテストを追加する(30分、信頼度は低く、バグがある可能性が高い)

「無駄」なのは、信頼できないコードを残すことのほうです。実際のテストがない動作するコードは、技術的負債です。

「TDD は教条的であり、実用的であるとは状況に適応することだ」

TDD こそが実用的です:

  • コミット前にバグを発見できる(事後のデバッグより速い)
  • リグレッションを防げる(テストが即座に破損を検出する)
  • 振る舞いを文書化できる(テストがコードの使い方を示す)
  • リファクタリングを可能にする(自由に変更でき、テストが破損を検出する)

「実用的」な近道は、本番環境でのデバッグを意味し、結果としてより遅くなります。

「後からのテストでも同じ目的は達成できる。大切なのは形式ではなく精神だ」

いいえ。後からのテストは「これは何をするのか?」に答え、テストファーストは「これは何をすべきか?」に答えます。

後からのテストは、あなたの実装に偏っています。要求されていることではなく、作ったものをテストします。発見されたエッジケースではなく、思い出せたエッジケースを検証します。

テストファーストは、実装前にエッジケースの発見を強制します。後からのテストは、あなたがすべてを思い出せたかを検証しますが、実際には思い出せていません。

後から書いた30分のテストは、TDD ではありません。カバレッジは得られても、テストが機能することの証明は失われます。

よくある言い訳

言い訳実際
「単純すぎてテストする必要がない」単純なコードも壊れます。テストには30秒しかかかりません。
「後でテストする」即座に通るテストは、何も証明しません。
「後からのテストでも同じ目標を達成できる」後からのテスト=「これは何をするのか?」。テストファースト=「これは何をすべきか?」。
「すでに手動でテスト済み」場当たり的 ≠ 体系的。記録がなく、再実行もできません。
「X時間を削除するのは無駄」サンクコストの誤謬です。検証されていないコードを残すことは、技術的負債です。
「参考として残し、先にテストを書く」結局それを適応させることになります。それは後からのテストです。削除とは削除することです。
「まず探索する必要がある」問題ありません。探索したコードは捨て、TDD で始めてください。
「テストが難しい=設計が不明確」テストの声に耳を傾けてください。テストしにくい=使いにくい。
「TDD は開発を遅くする」TDD はデバッグより速いです。実用的とはテストファーストのことです。
「手動テストのほうが速い」手動では、エッジケースを証明できません。変更のたびに再テストすることになります。
「既存のコードにはテストがない」既存のコードを改善しているのです。既存のコードにテストを追加してください。

危険信号 - 立ち止まり、最初からやり直してください

  • テストより先にコードを書く
  • 実装の後にテストを書く
  • テストが即座に通る
  • テストが失敗した理由を説明できない
  • テストを「後で」追加する
  • 「今回だけ」と正当化する
  • 「すでに手動でテストした」
  • 「後からのテストでも同じ目的を達成できる」
  • 「大切なのは形式ではなく精神だ」
  • 「参考として残す」または「既存のコードを適応させる」
  • 「すでにX時間費やした。削除するのは無駄だ」
  • 「TDD は教条的だ。私は実用的に行動している」
  • 「これは違う。なぜなら…」

これらはすべて、こう意味します: コードを削除し、TDD で最初からやり直してください。

例: バグ修正

バグ: 空のメールアドレスが受け入れられる

RED

test "rejects empty email" do
  user = User.new(email: "")
  assert_not user.valid?
  assert_includes user.errors[:email], "can't be blank"
end

RED の検証

$ bin/rails test test/models/user_test.rb
FAIL: expected "can't be blank" to be included in []

GREEN

class User < ApplicationRecord
  validates :email, presence: true
end

GREEN の検証

$ bin/rails test test/models/user_test.rb
1 runs, 1 assertions, 0 failures, 0 errors

REFACTOR 必要に応じて、複数フィールドのバリデーションを抽出してください。

検証チェックリスト

作業を完了とする前に、以下を確認してください。

  • 新しい関数/メソッドにはすべてテストがある
  • 実装前に、各テストが失敗することを確認した
  • 各テストは想定した理由(機能が未実装であるためであり、タイプミスではない)で失敗した
  • 各テストを通すために必要最小限のコードを書いた
  • すべてのテストが成功する
  • 出力がクリーンである(エラーや警告がない)
  • テストでは実際のコードを使っている(やむを得ない場合のみモックを使用)
  • エッジケースとエラーを網羅している

すべての項目にチェックを付けられませんか?TDD を省略しています。最初からやり直してください。

行き詰まったとき

問題解決策
テストの方法がわからない望ましい API を書きます。先にアサーションを書きます。人間のパートナーに相談します。
テストが複雑すぎる設計が複雑すぎます。インターフェースをシンプルにします。
すべてをモックする必要があるコードの結合度が高すぎます。依存性注入を使います。
テストのセットアップが膨大ヘルパーを抽出します。それでも複雑ですか?設計をシンプルにします。

統合のデバッグ

バグが見つかりましたか?そのバグを再現する失敗するテストを書きます。TDD サイクルに従います。テストによって修正が検証され、再発が防止されます。

テストなしでバグを修正してはいけません。

テストのアンチパターン

モックやテストユーティリティを追加する際は、よくある落とし穴を避けるために @testing-anti-patterns.md を読んでください。

  • 実際の動作ではなく、モックの動作をテストする
  • 本番クラスにテスト専用のメソッドを追加する
  • 依存関係を理解せずにモックする

モックを追加したりテストを書いたりする際は、Rails 固有のパターンについて @testing-strategy.md を読んでください。

最終ルール

Production code → test exists and failed first
Otherwise → not TDD

人間のパートナーの許可がない限り、例外は認められません。

コメント