mcpField
T
testing

testing

github.com/thinkoodleテスト・品質7179 回インストール8 週間前に更新
リポジトリ

testing について

Rails 開発者向けのテストスキルで、テスト種別の選択、スイートの構成、問題の調査を扱います。テストピラミッドを重視し、可能な場合はブラウザテストよりリクエストテストと fixtures を勧めます。

主な機能

  1. 01適切な Rails テスト種別の選択
  2. 02リクエスト・システムテストの作成
  3. 03並列テストと CI の設定
  4. 04DB 問題や不安定なテストの調査
  5. 05fixtures によるテストデータ管理

活用シーン

  1. 01Rails テストスイートの構築
  2. 02HTTP エンドポイントのリクエストテスト追加
  3. 03並列テストや CI の設定
  4. 04不安定なシステムテストの調査

インストール

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

$npx skills add https://github.com/thinkoodle/rails-skills --skill testing

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
ci.md
fixtures.md
helpers-and-assertions.md
parallel-testing.md
request-tests.md
system-tests.md

詳細

Rails テストエキスパート

Rails アプリケーション向けに、高速で信頼性が高く、保守しやすいテストスイートを構築します。このスキルでは、Rails のテストエコシステム、つまりテストの種類、インフラストラクチャ、どこで何をテストするかを扱います。

関連スキル: Minitest 固有のアサーション、フィクスチャの詳細、TDD ワークフロー、RSpec から Minitest への変換については、minitest スキルを参照してください。

このスキルを使う場面

  • Rails のテストスイートをセットアップまたは設定するとき
  • テストの種類(システム、リクエスト、モデル、ジョブ、メーラーなど)を選ぶとき
  • Capybara を使ってシステムテストを書くとき
  • 並列テストや CI パイプラインを設定するとき
  • 不安定なテストやテストデータベースの問題をデバッグするとき
  • 新機能のテストカバレッジを追加するとき

基本方針

テストピラミッドを守る
      /\       System Tests (FEW — critical user paths only)
     /  \
    /____\     Request/Integration Tests (MODERATE)
   /      \
  /________\   Unit Tests (MANY — models, services, policies, jobs)

エージェントによるミスのほとんどは、このピラミッドを逆転させることから生じます。 あらゆる機能のシステムテストを書くことは、最もよくある間違いです。システムテストはユニットテストより10〜100倍遅くなります。不安定なシステムテストが1つあるだけで、モデルテスト50件分以上の時間が無駄になります。

基本ルール
  1. 実装ではなく動作をテストする — メソッド呼び出しではなく、結果を検証します
  2. ファクトリーではなくフィクスチャを使う — 10〜100倍高速で、連鎖的なレコード作成がありません
  3. システムテストよりリクエストテストを優先する — JavaScript に依存するフローに限り、システムテストを使います
  4. 各テストを独立させる — 共有状態やテスト実行順への依存をなくします
  5. 包括的なカバレッジより高速なフィードバックを重視する — 頻繁に実行される高速なスイートは、実行されない低速なスイートに勝ります

適切なテストの種類を選ぶ

これは最も重要な判断です。選択を誤ると、低速でもろいテストを書くことになり、時間を無駄にします。

テストの種類基底クラス速度用途
モデルActiveSupport::TestCase⚡ 高速バリデーション、スコープ、ビジネスロジック、コールバック
サービス/POROActiveSupport::TestCase⚡ 高速サービスオブジェクト、フォームオブジェクト、クエリオブジェクト
ジョブActiveJob::TestCase⚡ 高速ジョブの動作、エンキュー、リトライ
メーラーActionMailer::TestCase⚡ 高速メールの内容、宛先、添付ファイル
ヘルパーActionView::TestCase⚡ 高速ビューヘルパーの出力
リクエストActionDispatch::IntegrationTest🔶 中速HTTP リクエスト/レスポンスのサイクル、API エンドポイント
インテグレーションActionDispatch::IntegrationTest🔶 中速ブラウザーを使わない複数ステップのワークフロー
チャンネルActionCable::Channel::TestCase⚡ 高速チャンネルのサブスクリプション、ストリーム
コネクションActionCable::Connection::TestCase⚡ 高速WebSocket 認証、接続識別子
システムActionDispatch::SystemTestCase🐌 低速JavaScript 操作、重要な E2E ユーザーフロー
判断フロー
Does it test business logic with no HTTP needed?
  → Model / Service test

Does it test an HTTP endpoint's response?
  → Request test

Does it require a real browser (JS, complex UI)?
  → System test (keep these minimal)

Does it test email content or delivery?
  → Mailer test

Does it test a background job?
  → Job test

Does it test a view helper method?
  → Helper test

Does it test a WebSocket channel?
  → Channel test

テストディレクトリの構成

test/
├── test_helper.rb                    # Global config
├── application_system_test_case.rb   # System test base (created on first system test)
├── controllers/                      # Controller/functional tests (legacy — prefer request tests)
├── fixtures/                         # YAML fixture data
│   ├── users.yml
│   ├── files/                        # File fixtures for uploads
│   └── action_text/rich_texts.yml    # Action Text fixtures
├── helpers/                          # View helper tests
├── integration/                      # Integration tests
├── jobs/                             # Job tests
├── mailers/                          # Mailer tests
│   └── previews/                     # Mailer previews (not tests, but useful)
├── models/                           # Model tests (bulk of your tests)
├── channels/                         # Action Cable channel tests
│   └── application_cable/            # Connection tests
├── requests/                         # Request tests (preferred over controllers/)
├── services/                         # Service object tests
├── system/                           # System tests (keep small)
└── support/                          # Shared test helpers

テストランナー

基本的なコマンド
# Run everything (excludes system tests)
bin/rails test

# Run system tests separately
bin/rails test:system

# Run ALL tests including system
bin/rails test:all

# Run a single file
bin/rails test test/models/user_test.rb

# Run a specific test by line number
bin/rails test test/models/user_test.rb:42

# Run a line range
bin/rails test test/models/user_test.rb:10-30

# Run by name pattern
bin/rails test -n /password/

# Run a directory
bin/rails test test/models/

# Fail fast — stop on first failure
bin/rails test --fail-fast

# Verbose output
bin/rails test -v

# Profile slow tests
bin/rails test --profile

# Set seed for reproducibility
bin/rails test --seed 12345
主なフラグ
フラグ用途
-n PATTERN名前でテストを絞り込みます(文字列または/regex/)
-f / --fail-fast最初の失敗で停止します
-v / --verbose各テスト名を表示します
-b / --backtraceアプリケーションの行だけでなく、完全なバックトレースを表示します
--profile [N]実行時間が最も長いテストをN件表示します(既定値は10)
-p / --pride虹色の出力 🌈
-S / --skip CODES報告する種類をスキップします(例:エラーの場合はE)

リクエストテスト(HTTP テストで推奨)

リクエストテストは、ミドルウェアスタックを通じた完全な HTTP リクエストをシミュレートします。新しいコードでは、コントローラーテストの代わりにこれを使ってください。

class ArticlesRequestTest < ActionDispatch::IntegrationTest
  setup do
    @user = users(:admin)
    @article = articles(:published)
  end

  test "GET /articles returns success" do
    get articles_url
    assert_response :success
  end

  test "POST /articles creates with valid params" do
    sign_in @user
    assert_difference "Article.count", 1 do
      post articles_url, params: { article: { title: "New", body: "Content" } }
    end
    assert_redirected_to article_url(Article.last)
  end

  test "POST /articles rejects invalid params" do
    sign_in @user
    assert_no_difference "Article.count" do
      post articles_url, params: { article: { title: "" } }
    end
    assert_response :unprocessable_entity
  end
end
リクエスト後に利用できるもの
  • response / response.body / response.parsed_body
  • cookies、flash、session
  • @controller、@request、@response

JSON API テスト、Turbo Stream テスト、ファイルアップロード、認証パターン、DOM アサーションについては、references/request-tests.md を参照してください。

システムテスト(必要な場合に限って使用)

システムテストは、Capybara を通じて実際のブラウザーで実行します。JavaScript や複雑な UI 操作が必要な重要なフローに限って使用してください。

システムテストを書く場合

✅ ユーザー登録/ログインのフロー ✅ チェックアウト/決済のフロー ✅ 複雑な JS インタラクション(ドラッグ&ドロップ、モーダル、ライブ検索) ✅ Turbo/Stimulus に依存する機能 ✅ プレビュー付きのファイルアップロード

システムテストを書かない場合

❌ CRUD 操作(リクエストテストを使用) ❌ バリデーションエラーの表示(リクエストテストと assert_select を使用) ❌ API エンドポイント(リクエストテストを使用) ❌ ブラウザーなしでテストできるもの

設定
# test/application_system_test_case.rb
require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :headless_chrome
end

ドライバーのオプション:

ドライバーブラウザー用途
:selenium + :headless_chromeChrome(ヘッドレス)デフォルト、CI に適しています
:selenium + :headless_firefoxFirefox(ヘッドレス)代替
:selenium + :chromeChrome(表示)デバッグ
:cupriteCDP 経由の Chromeより高速、Selenium のオーバーヘッドなし
システムテストを書く
require "application_system_test_case"

class CheckoutTest < ApplicationSystemTestCase
  test "user completes checkout" do
    user = users(:buyer)
    product = products(:widget)

    visit new_session_path
    fill_in "Email", with: user.email
    fill_in "Password", with: "password"
    click_on "Sign in"

    visit product_path(product)
    click_on "Add to Cart"
    click_on "Checkout"

    fill_in "Card number", with: "4242424242424242"
    click_on "Pay"

    assert_text "Order confirmed"
  end
end
不安定なシステムテストを避ける
  • assert_text、assert_selector は自動的に待機します。page.has_css? の代わりに使用してください
  • それでも不安定な場合は、明示的な待機を追加します:assert_text "Done", wait: 5
  • sleep は絶対に使用しないでください。常に Capybara の待機対応アサーションを使用してください
  • デバッグには take_screenshot を使用します。失敗したテストでは tmp/screenshots/ に自動でスクリーンショットが保存されます

Capybara DSL のリファレンス、Cuprite のセットアップ、JS インタラクションのパターン、Turbo/Stimulus のテストについては、references/system-tests.md を参照してください。

メーラーテスト

class UserMailerTest < ActionMailer::TestCase
  test "welcome email" do
    email = UserMailer.welcome(users(:new_user))
    assert_emails 1 do
      email.deliver_now
    end
    assert_equal ["[email protected]"], email.from
    assert_equal [users(:new_user).email], email.to
    assert_equal "Welcome!", email.subject
    assert_match users(:new_user).name, email.body.to_s
  end
end

リクエストテストから送信する場合: assert_emails 1 { post users_url, params: {...} } キューに登録する場合: assert_enqueued_email_with UserMailer, :welcome, args: [user] { ... }

マルチパートメール、添付ファイル、パラメーター化されたメーラー、プレビューについては、references/helpers-and-assertions.md を参照してください。

ジョブテスト

class ProcessOrderJobTest < ActiveJob::TestCase
  test "charges the account" do
    perform_enqueued_jobs do
      ProcessOrderJob.perform_later(orders(:pending))
    end
    assert orders(:pending).reload.charged?
  end
end

ほかのコードからのキュー登録を検証する場合:

class OrderTest < ActiveSupport::TestCase
  include ActiveJob::TestHelper

  test "#place enqueues processing job" do
    assert_enqueued_with(job: ProcessOrderJob) do
      orders(:new).place!
    end
  end
end

ジョブキューのアサーション、フィルタリング、チェーンジョブ、例外のテストについては、references/helpers-and-assertions.md を参照してください。

ヘルパーテスト

class CurrencyHelperTest < ActionView::TestCase
  test "formats USD" do
    assert_equal "$10.00", format_price(1000, "USD")
  end

  test "handles zero" do
    assert_equal "$0.00", format_price(0, "USD")
  end
end

Action Cable テスト

# Channel test
class ChatChannelTest < ActionCable::Channel::TestCase
  test "subscribes to room stream" do
    subscribe room: "general"
    assert subscription.confirmed?
    assert_has_stream "chat_general"
  end
end

# Connection test
class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase
  test "connects with valid user" do
    cookies.signed[:user_id] = users(:admin).id
    connect
    assert_equal users(:admin).id, connection.current_user.id
  end

  test "rejects without auth" do
    assert_reject_connection { connect }
  end
end

ブロードキャストのアサーション(すべてのテストで使用可能): include ActionCable::TestHelper を追加し、assert_broadcast_on、assert_broadcasts を使用します。

ストリームのアサーション、カスタムチャンネルメソッド、モデルからのブロードキャストテストについては、references/helpers-and-assertions.md を参照してください。

テストデータベースの管理

スキーマの同期
# After migrations, sync test DB schema
bin/rails db:test:prepare

# Or recreate from scratch
bin/rails db:test:purge db:test:prepare

# Rails auto-checks for pending migrations before tests run
トランザクション

デフォルトでは、各テストはデータベーストランザクション内で実行され、完了後にロールバックされます。これにより、手動でクリーンアップしなくてもテストが分離されます。

必要に応じて無効にします(例:マルチスレッドコードのテスト):

class ThreadedTest < ActiveSupport::TestCase
  self.use_transactional_tests = false

  teardown do
    # Manual cleanup needed!
    User.delete_all
  end
end

並列テスト

プロセスベース(デフォルト)
# test/test_helper.rb
class ActiveSupport::TestCase
  parallelize(workers: :number_of_processors)
end

ワーカーごとに個別のテストデータベース(test-database-0、test-database-1 など)を作成します。

# Override worker count
PARALLEL_WORKERS=4 bin/rails test
スレッドベース
class ActiveSupport::TestCase
  parallelize(workers: :number_of_processors, with: :threads)
end

JRuby/TruffleRuby を使用する場合や、プロセスのフォークに時間がかかる場合に使用します。

ワーカーごとのセットアップには、parallelize_setup/parallelize_teardown フックを使用します。デフォルトのしきい値は 50 テストです(それ未満では並列化されません)。config.active_support.test_parallelization_threshold = 100 でカスタマイズできます。

プロセスとスレッドの比較、フック、並列テストでよくある問題については、references/parallel-testing.md を参照してください。

CI の設定

# config/environments/test.rb
config.eager_load = ENV["CI"].present?
# CI steps
bin/rails db:test:prepare
bin/rails test                    # Unit + request tests
bin/rails test:system             # System tests (separate job recommended)

CI のヒント: PARALLEL_WORKERS を使用して並列化する、システムテストを専用の CI ジョブに分ける、gem/node_modules をキャッシュする、ヘッドレス Chrome を使用する、Zeitwerk の eager-load テストを追加する。

GitHub Actions の YAML 全体と CI のパターンについては、references/ci.md を参照してください。

時間に依存するテスト

freeze_time do ... end                                    # Freeze at current moment
travel_to Time.zone.local(2024, 1, 15) do ... end        # Travel to specific time
travel 3.days do ... end                                  # Travel forward
travel_back                                               # Return to real time

時間テストの詳しいパターン、よくある落とし穴、不安定なテストの修正については、references/parallel-testing.md を参照してください。

避けるべきアンチパターン

  1. システムテストが多すぎる — ブラウザーが不要なら、リクエストテストを使います
  2. Rails 自体をテストする — validates :name, presence: true が機能することをテストしてはいけません。テストするのは独自のビジネスロジックです
  3. 実装をテストする — assert_received(:method) はテストを内部実装に結び付けます
  4. ファクトリーの連鎖 — Factory Bot が1つのテストのために15件のレコードを作成する場合は、フィクスチャを使います
  5. 変更可能な状態の共有 — あるテストで設定したインスタンス変数が別のテストに漏れます
  6. sleep による待機 — システムテストで sleep 2 を使わず、Capybara 組み込みの待機機能を使います
  7. タイムスタンプの厳密なアサーション — assert_equal Time.current, record.created_at は不安定です
  8. プライベートメソッドのテスト — テストするのはパブリックインターフェースだけです
  9. 過剰なモック — すべてをモックすると、コードではなくモックをテストすることになります
  10. 否定的なテストがない — 失敗または拒否されるべきケースも必ずテストします

クイックリファレンス:何をどこでテストするか

対象テストの種類例
モデルのバリデーションモデルテストrefute User.new(email: nil).valid?
モデルのスコープモデルテストassert_includes User.active, @user
ビジネスロジックのメソッドモデル/サービスのテストassert_equal 42, order.total
API エンドポイントリクエストテストget api_users_url, as: :json
ページが正しくレンダリングされることリクエストテストget root_url; assert_response :success
フォーム送信リクエストテストpost articles_url, params: {...}
認証/認可リクエストテストget admin_url; assert_response :redirect
メールの内容メーラーのテストassert_equal "Welcome", email.subject
メールが送信されることリクエストテストassert_emails 1 { post ... }
ジョブの動作ジョブテストperform_enqueued_jobs { MyJob.perform_later }
ジョブがエンキューされることモデル/リクエストテストassert_enqueued_with(job: MyJob) { ... }
JS 依存のフローシステムテストclick_on "Submit"; assert_text "Done"
ビューヘルパーヘルパーテストassert_equal "$10", format_price(10)
WebSocket チャネルチャネルテストsubscribe; assert_has_stream "room_1"

コメント