ActiveRecordテストの通知アサーションをNotificationAssertionsヘルパーで統一

rails/rails

#57320に続くリファクタリングとして、activerecordテストスイート内に残っていた手動のActiveSupport::Notifications.subscribeパターンをNotificationAssertionsヘルパーへ移行した。対象はテストコードのみで、動作変更はない。

背景

ActiveSupport::Notificationsの手動サブスクリプションパターンには、テストコードを複雑にする固有の問題があった。コールバック内でフラグを立てて後から検証する方式(asserted = trueパターン)や、ensure節でのサブスクライバ解除忘れリスク、さらにコールバック内インラインアサーションによる可読性の低下が挙げられる。

#56989を起点にこの整理が始まり、trilogy_adapter_test.rbinstrumentation_test.rbquery_cache_test.rb#57320で移行済みだった。本PRはその続きとして、asynchronous_queries_test.rbconnection_pool_test.rbrelation/load_async_test.rbの3ファイルを対象とする。

技術的な変更

capture_notificationsが適用可能なケースでは、手動サブスクリプション全体をブロック呼び出しに置き換えることで、イベントの収集・解除・アサーションを宣言的に記述できる。

asynchronous_queries_test.rbtest_async_query_foreground_fallbackでは、サブスクライバコールバック内でフラグを設定していた従来の方式が、capture_notificationsのブロックで包む形式に変わった。

変更前:

status = {}
subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
  if event.payload[:sql] == "SELECT * FROM does_not_exists"
    status[:executed] = true
    status[:async] = event.payload[:async]
  end
end
# ... テスト処理 ...
assert_equal true, status[:executed]
assert_equal false, status[:async]
ensure
  ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber

変更後:

events = capture_notifications("sql.active_record") do
  # ... テスト処理 ...
end

event = events.find { _1.payload[:sql] == "SELECT * FROM does_not_exists" }
assert_not_nil event
assert_equal false, event.payload[:async]

connection_pool_test.rbの2つのテストも同様に移行された。payloads配列への手動蓄積とensure節でのサブスクライバ解除が削除され、events.first.payloadへの直接アクセスに置き換えられている。ensure節のActiveSupport::Notifications.unsubscribe呼び出しも不要になり、コード量が削減された。

一方、load_async_test.rbではスレッド境界をまたぐイベント観測が必要なテストはActiveSupport::Notifications.subscribeを維持している。capture_notificationsが内部で使用するActiveSupport::Notifications.subscribedはスレッドローカルであるため、非同期クエリスレッドから発行されるsql.active_recordイベントを確実に捕捉できない。このファイルでの変更はstatus[:executed] = trueフラグの削除と、関連するアサーション文言の微修正にとどまる。

設計判断

適用範囲をスレッドセーフな場合に限定するという方針が明示的に採られている。capture_notificationsは利便性が高いが、スレッドローカルな購読メカニズムに基づく制約があり、全テストに一律適用することはできない。

PRではこのトレードオフを「安全である場合に限り移行する(In each case where it is safe)」と明記しており、load_async_test.rbの非同期スレッド観測テストを意図的に除外している。capture_notificationsを使える箇所と使えない箇所を明確に分離することで、テストの信頼性を損なわずにリファクタリングを進めている。

まとめ

本PRは、NotificationAssertionsヘルパーへの移行をActiveRecordテストスイート全体に段階的に適用するシリーズの一環だ。ensure節でのサブスクライバ管理という手続き的なコードが宣言的なブロック構文に置き換わることで、テストの意図が読み取りやすくなる。スレッドローカル制約を認識した上で適用範囲を選別している点は、テストインフラの設計理解を深める実例でもある。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
47906712

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)、背景、技術的な変更、設計判断(各論)、まとめ(結論)の3部構成が明確で、理想的な記事構成です。

カスタムMarkdown構文 ⚠ WARNING

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

ファイル名付きシンタックスハイライトは正しく使用されていますが、GitHubのPR番号リンク記法がガイドラインの推奨形式(`[#123](URL)`)と一部異なっています(例:`[PR #57481]`)。ただし、リンクは機能しており、内容理解には影響ありません。

対象読者への適合性 ✓ PASS

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

ActiveRecordのテストスイートやActiveSupport::Notificationsに関する知識を前提としており、専門知識を持つエンジニアという対象読者に完全に適合しています。

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

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

各セクション、各パラグラフが総論→各論の構造で書かれており、トピックセンテンスも明確です。非常に読みやすく、構成が優れています。

Diff内容との照合 ✓ PASS

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

記事内のコードブロック(変更前・変更後)は、提供されたDiffの内容とファイル名を正確に反映しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「NotificationAssertions」「スレッドローカル」「サブスクライバ」など、関連する技術用語を正確かつ適切に使用しています。

説明の技術的正確性 ✓ PASS

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

「`capture_notifications`がスレッドローカルであるため一部のテストでは使えない」という説明は、PR Descriptionの記述に基づいており、技術的に正確です。

事実の突合 ✓ PASS

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

記事で述べられている変更の背景、対象ファイル、設計上の判断は、すべてPRのTitleとDescriptionで裏付けられており、ハルシネーションは検出されませんでした。

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

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

PR番号(#57481, #57320, #56989)やファイル名がすべて正確に記載されています。

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

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

記事のタイトルは、PRの主題「`NotificationAssertions`ヘルパーで`activerecord`テストを簡素化する」を的確に要約しています。

外部知識の正確性 ✓ PASS

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

PRで言及されていない外部知識(バージョンのサポート状況、リリース日程など)の追加はなく、提供された情報に忠実です。

時間表現の正確性 ✓ PASS

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

時間表現に歪曲はなく、PRで完了した変更を事実として正確に記述しています。