ActiveJob キューをテスト間でクリアしてジョブリークを防止

rspec/rspec-rails

リード文: have_been_enqueuedhave_been_performed がテスト間で残存ジョブを誤検知する問題を解消するため、RSpec のジョブ例グループにキューのクリア処理を導入しました。これにより、Rails の実装と同等のテスト分離が保証されます。

背景

このセクションでは、ジョブリークが発生した経緯とその影響を説明します。

have_been_enqueuedhave_been_performed はキューアダプターの enqueued_jobs / performed_jobs 配列を直接参照しますが、テスト環境では同一の TestAdapter インスタンスが再利用されるため、ジョブが例間で蓄積されてしまいます。その結果、後続の例が以前の例で残ったジョブを見て失敗するという不具合が報告されました(#2900)。

RSpec 自身のマッチャーは ActiveJob::Base.queue_adapter = :testbefore フックで再設定しているため、内部的には毎例新しい TestAdapter が生成され、リークが隠蔽されていました。この実装差が実アプリでの挙動と乖離していることが問題視されました。

以上の背景から、テストフレームワーク側でキュー状態を例毎にリセットする必要があると判断されました。

技術的な変更

このセクションでは、具体的なコード変更点とその動作を示します。

RailsExampleGroupActiveJob::TestHelper をインクルード することで、テスト実行前にキューをリセットするヘルパーメソッドが利用可能になります。変更は lib/rspec/rails/example/rails_example_group.rb に以下のように追加されました。

module RailsExampleGroup
  module ClassMethods
    def included(base)
      super
      include RSpec::Rails::TaggedLoggingAdapter
      include ActiveSupport::CurrentAttributes::TestHelper
      include ActiveSupport::ExecutionContext::TestHelper
+
+      if RSpec::Rails::FeatureCheck.has_active_job?
+        include ActiveJob::TestHelper
+      end
    end
  end
end

ActiveJob::TestHelper が提供する clear_enqueued_jobsclear_performed_jobsbefore フックで自動的に呼び出され、テスト例開始時にキューが空になることを保証します。

テストケースの追加 により、修正が正しく機能することが確認されています。spec/rspec/rails/example/rails_example_group_spec.rb に追加された例では、2 つの例を順に実行し、2 番目の例でキューが空であることを検証しています。

it 'will not leak enqueued ActiveJob jobs between examples', skip: !RSpec::Rails::FeatureCheck.has_active_job? do
  original_adapter = ActiveJob::Base.queue_adapter
  original_logger = ActiveJob::Base.logger
  ActiveJob::Base.queue_adapter = :test
  ActiveJob::Base.logger = Logger.new(nil)

  leaky_job = Class.new(ActiveJob::Base) do
    def perform; end
    def self.name; 'LeakyJob' end
  end

  group = RSpec::Core::ExampleGroup.describe('A group', order: :defined) do
    include RSpec::Rails::RailsExampleGroup

    it 'enqueues a job' do
      leaky_job.perform_later
      expect(enqueued_jobs.size).to eq(1)
    end

    it 'does not see the job enqueued in the prior example' do
      expect(enqueued_jobs).to be_empty
    end
  end

  expect(group.run(failure_reporter) ? true : failure_reporter.exceptions).to be true
ensure
  ActiveJob::Base.queue_adapter = original_adapter
  ActiveJob::Base.logger = original_logger
end

これにより、enqueued_jobsperformed_jobs が例毎にリセットされ、マッチャーが正しい状態を評価できるようになります。

設計判断

このセクションでは、採用された実装アプローチの設計意図とトレードオフを整理します。

RailsExampleGroup へのインクルード を選択した理由は、ジョブ例だけでなく全ての Rails 例で同様のクリア動作が必要になる可能性を考慮し、共通基盤に機能を集約することで重複実装を防ぐためです。JobExampleGroup に限定すると、将来的に別のヘルパーでもキューリセットが必要になる場合に修正が散在するリスクがあります。

before フックの採用 は、テスト実行後にキューを残すことで around フック等からの検査を可能にしつつ、例開始時点で常にクリーンな状態を保証するというバランスを取っています。after フックでも実装は可能ですが、ユーザーが after フック内でキューを参照したいケースを考慮し、before が最も汎用的と判断されました。

respond_to? ガード を残したのは、ユーザーがテストアダプタ以外のキューアダプタに切り替えている場合でもエラーにならないように安全性を確保するためです。この点は既存の ActiveJob::TestHelper と同様の実装パターンを踏襲しています。

まとめ

本 PR は、RSpec が Rails の ActiveJob::TestHelper と同等のキューリセット機構を提供することで、テスト間のジョブリークを防止しました。RailsExampleGroupActiveJob::TestHelper をインクルードし、before フックでキューをクリアする設計は、後方互換性と拡張性を両立させた選択です。結果として、have_been_enqueued 系マッチャーが正確に動作し、テスト信頼性が向上します。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
9c3a4bba

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文、背景、技術的な変更、設計判断、まとめの5セクションが揃い、総論→各論→結論の流れが明確です。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックは `ruby:ファイルパス` 形式で正しく記述されていますが、PRリンクが `PR #2901` という形式で、求められる `[#2901](URL)` 形式と異なります。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Rails と ActiveJob に精通したエンジニア向けの内容で、初心者向けの余計な説明はありません。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは総論・各論・結論のパラグラフ構成になり、トピックセンテンスが先頭に配置されています。段落は適切な長さで空行で区切られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは提供された Diff と完全に一致し、ファイル名・変更内容とも合致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

ActiveJob、TestHelper、enqueued_jobs などの用語は正しく使用され、誤用は見られません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

変更理由や動作説明が PR の記述と整合しており、技術的に誤った主張はありません。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

全ての主張は PR タイトル、説明、Diff で裏付けられており、憶測や捏造はありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR 番号 #2901 と Issue 番号 #2900 が正しく記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR タイトル「Clear ActiveJob queues between examples in job specs」の意味を日本語で正確に表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

バージョンサポートやリリース日程など、PR に記載されていない外部情報は含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現の歪曲はなく、PR と一致しています。