非トランザクションテスト後のフィクスチャキャッシュを適切にリセットする

rails/rails

ActiveRecordのテストフィクスチャにおいて、非トランザクションテスト実行後にActiveRecord::FixtureSet.reset_cacheが呼ばれないバグが修正されました。これにより、トランザクションテストと非トランザクションテストが混在する環境での実行順序に依存したフィクスチャの不整合が解消されます。

背景

この修正は、#57326(RailsのテストスイートをMinitest風のスイート単位ではなくテスト全体でランダム順に実行するmegatest移行)から抽出されたものです。megatestは、テストの実行順をスイート単位ではなく完全にランダム化するため、トランザクションフィクスチャを無効にしたテスト(非トランザクションテスト)と通常のトランザクションテストが交互に実行されるケースが大幅に増加します。

Minitestの従来の実行モデルでは、同じテストクラス内のテストが連続して実行されるため、非トランザクションテストの後に別クラスのトランザクションテストが続くことは稀でした。しかしmegatestの完全ランダム実行ではこの前提が崩れ、フィクスチャキャッシュの状態管理が問題になりました。

技術的な変更

問題の核心は、ActiveRecord::FixtureSet.reset_cacheの呼び出し位置が誤っていたことです。このメソッドはフィクスチャのファイルパース結果などのキャッシュをクリアしますが、従来は非トランザクションテストのsetup_fixtures内でのみ呼ばれており、invalidate_already_loaded_fixturesメソッドには含まれていませんでした。

変更前:

if run_in_transaction?
  @loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key]
  unless @loaded_fixtures
    @@already_loaded_fixtures.clear
    @loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key] = load_fixtures(config)
  end
  setup_transactional_fixtures
else
  # Load fixtures for every test.
  ActiveRecord::FixtureSet.reset_cache
  invalidate_already_loaded_fixtures
  @loaded_fixtures = load_fixtures(config)
end

変更後:

if run_in_transaction?
  unless (@loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key])
    invalidate_already_loaded_fixtures
    @loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key] = load_fixtures(config)
  end
  setup_transactional_fixtures
else
  # Load fixtures for every test.
  invalidate_already_loaded_fixtures
  @loaded_fixtures = load_fixtures(config)
end

# ...

def invalidate_already_loaded_fixtures
  ActiveRecord::FixtureSet.reset_cache
  @@already_loaded_fixtures.clear
end

ActiveRecord::FixtureSet.reset_cacheinvalidate_already_loaded_fixturesメソッド内に移動することで、非トランザクションテストが実行された後にトランザクションテストが続く場合でも、@@already_loaded_fixturesクリアと同時にフィクスチャのファイルキャッシュもリセットされるようになりました。また、トランザクションテストのキャッシュミス時にもinvalidate_already_loaded_fixturesを呼び出すよう変更され、両コードパスで一貫したキャッシュ管理が行われます。

付随して、fixtures_test.rb内の2つのテストケースで、ActiveRecord::TestCaseを継承していたクラスがActiveSupport::TestCaseActiveRecord::TestFixturesをincludeする形式へ変更されています。これにより、テスト対象のコードパスがActiveRecord::TestCaseの追加の前提に依存しなくなり、ActiveRecord::TestFixturesモジュールそのものの動作を直接検証できます。

設計判断

invalidate_already_loaded_fixturesを単一の責務を持つメソッドに整理するアプローチが採用されています。変更前はFixtureSet.reset_cache@@already_loaded_fixtures.clearの2つの操作が別々の場所に分散しており、「フィクスチャの無効化」が不完全にしか実行されないパスが存在していました。

この修正では「フィクスチャキャッシュの無効化」という概念をinvalidate_already_loaded_fixturesメソッドに集約し、2つのキャッシュ層(FixtureSetのファイルパースキャッシュと@@already_loaded_fixturesのロード済みフィクスチャキャッシュ)を常にセットで操作することを保証しています。呼び出し側がどちらかの操作を忘れるという人的ミスを構造的に防いでいます。

また、tools/support/leak_checker.rbへの変更も含まれています。@__leak_checker_before_envが設定されていない場合にafter_teardownが早期リターンするガード節が追加されており、before_setupが呼ばれていないテストでのnil参照を防いでいます。

まとめ

本PRは、2つのキャッシュ層の無効化操作をinvalidate_already_loaded_fixturesに集約することで、テスト実行順序に関わらずフィクスチャキャッシュの整合性を保証します。megatestへの移行という文脈で顕在化したバグですが、本質的にはMinitest環境でも実行順次第で同様の問題が起こりうる設計上の欠陥の修正であり、フィクスチャの信頼性向上に貢献します。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
022f37a5

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)、背景・技術詳細・設計判断(各論)、まとめ(結論)という構成が明確で、ガイドラインを完全に満たしています。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```ruby:path/to/file.rb)とGitHubへのリンク記法([#57326](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

ActiveRecordの内部実装に関する知識を前提としており、専門知識を持つエンジニアという対象読者に完全に適合しています。

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

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

各セクション、各パラグラフが「総論→各論」の構成になっており、トピックセンテンスが先頭に置かれているため、非常に読みやすい構造です。

Diff内容との照合 ✓ PASS

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

記事で引用されているコードブロック(変更前・変更後)は、提供されたDiffの内容と完全に一致しており、正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`ActiveRecord::FixtureSet`, `megatest`, `run_in_transaction?`などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

キャッシュリセットのロジックが変更された理由と、その変更がもたらす効果について、技術的に正確かつ論理的に説明されています。

事実の突合 ✓ PASS

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

記事内のすべての主張(megatestの背景、コード変更の意図など)は、提供されたPRのDescriptionやDiff内容によって裏付けられています。

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

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

PR番号(#57352, #57326)やその他の固有名詞(メソッド名、クラス名)はすべて正確に記載されています。

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

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

記事のタイトルはPRのタイトル「Properly reset fixtures cache after non-transactional tests」を忠実に反映しており、内容も完全に一致しています。

外部知識の正確性 ✓ PASS

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

PR情報に基づかないバージョン情報やリリース予定などの外部知識は含まれていません。背景説明もPRの文脈に沿っています。

時間表現の正確性 ✓ PASS

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

時間表現に関する不正確な記述や歪曲は見られませんでした。