`NotificationAssertions` ヘルパーによるActiveRecordテストの簡素化

rails/rails

ActiveSupport::Notifications.subscribe と手動フラグ管理によるテストパターンを、assert_notification ヘルパーで置き換えることで、ActiveRecordの通知テストが大幅に簡潔になりました。

背景

#56989 で開始されたActiveRecordテストの NotificationAssertions 対応を、残存していたファイルにも適用したのが本PRです。対象は trilogy_adapter_test.rbinstrumentation_test.rbquery_cache_test.rb の3ファイルで、いずれも ActiveSupport::Notifications.subscribe / subscribed を使った手動購読パターンが残っていました。

従来のテストは「購読コールバック内でアサーションを行い、asserted = true フラグで実際に検証が実行されたかを確認する」という多段構造になっていました。このパターンは冗長なだけでなく、ensure ブロックでの手動 unsubscribe を忘れた場合のリークリスクや、コールバック内のアサーション失敗がテスト全体のフローに与える影響など、潜在的な問題を抱えていました。

技術的な変更

assert_notification ヘルパーへの移行により、通知の検証ロジックが購読ブロックの外に切り出され、テストの意図が明確になりました。

instrumentation_test.rb における変更が典型的なパターンです。変更前は asserted フラグ、手動購読・解除、コールバック内アサーションが混在していましたが、変更後は通知オブジェクトを受け取って外側でアサーションするだけになりました。

変更前:

def test_payload_without_an_open_transaction
  asserted = false

  subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
    if event.payload.fetch(:name) == "Book Count"
      assert_nil event.payload.fetch(:transaction)
      asserted = true
    end
  end

  Book.count

  assert asserted
ensure
  ActiveSupport::Notifications.unsubscribe(subscriber)
end

変更後:

def test_payload_without_an_open_transaction
  notification = assert_notification("sql.active_record", name: "Book Count") do
    Book.count
  end

  assert_nil notification.payload.fetch(:transaction)
end

assert_notification はイベント名とペイロードの条件をキーワード引数で受け取り、ブロック内で発火した通知の中から条件に合致する最初のイベントを返します。asserted フラグと ensure ブロックが不要になり、コードが約半分の行数に削減されています。

trilogy_adapter_test.rb では、ペイロードの詳細な検証が必要なケースで assert_notification の戻り値を使い、取得した notification.payload に対してまとめてアサーションを行うパターンが採用されました。変更前は巨大なコールバックブロック内にすべてのアサーションが詰め込まれていましたが、変更後は通知の取得と検証が明確に分離されています。

query_cache_test.rb では、キャッシュされたクエリ(cached: true)を対象とする通知のフィルタリングも、ペイロード条件をヘルパーのキーワード引数として渡すだけで表現できるようになりました。

設計判断

assert_notification にペイロード条件をキーワード引数で渡す設計が、コールバック内の条件分岐(if event.payload[:name] == "Book Count")を置き換えています。

この設計により、テストの「どの通知を対象とするか」と「その通知の何を検証するか」が明確に分離されます。コールバック内でのフィルタリングと検証の混在は、条件が合致しなかった場合に asserted フラグの確認まで問題が発覚しないという遅延検出の問題がありました。assert_notification はブロック実行後に即座に対象通知を特定・返却するため、検証失敗の発見が早期化されます。

また、ensure による手動 unsubscribe が不要になることで、テストの後始末を開発者が意識する必要がなくなり、購読リークのリスクが根本的に排除されています。

まとめ

本PRは機能変更を伴わない純粋なテストリファクタリングですが、assert_notification ヘルパーへの統一によって、通知テストの記述パターンがコードベース全体で標準化されました。#56989 と合わせて、ActiveRecordのテスト群における ActiveSupport::Notifications の手動購読パターンが一掃され、テストの可読性と安全性が向上しています。

記事メタデータ

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

この記事は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```)とPR番号のリンク記法([#...](URL))がガイドラインに沿って正しく使用されています。

対象読者への適合性 ✓ PASS

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

ActiveRecordやテストヘルパーに関する専門用語を前提としており、冗長な初心者向け解説がないため、対象読者である専門エンジニアに適した内容になっています。

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

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

各セクションが総論→各論の構成で書かれ、各パラグラフはトピックセンテンスで始まり、1段落1トピックの原則が守られています。段落の長さも適切で、非常に読みやすい文章構造です。

Diff内容との照合 ✓ PASS

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

記事内で引用されている `instrumentation_test.rb` の変更前後のコードは、提供されたDiff情報と完全に一致しており、正確に内容を反映しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`NotificationAssertions`, `assert_notification`, `ActiveSupport::Notifications.subscribe`, `payload` といった技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

`assert_notification` ヘルパーの機能や、従来の購読パターンの問題点に関する説明は、コードの変更内容と整合性が取れており、技術的に正確です。

事実の突合 ✓ PASS

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

PRの背景(#56989のフォローアップ)、対象ファイル名、変更の主旨など、記事内のすべての主張はPR DescriptionやDiffの内容によって裏付けられており、ハルシネーションは検出されませんでした。

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

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

PR番号(#57320, #56989)や各種固有名詞(ファイル名、メソッド名など)はすべて正確に記載されています。

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

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

記事のタイトル「`NotificationAssertions` ヘルパーによるActiveRecordテストの簡素化」は、PRのタイトル「Simplify more activerecord tests with NotificationAssertions helpers」の内容を的確に日本語で表現しており、主題と完全に一致しています。

外部知識の正確性 ✓ PASS

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

記事には、PR情報に基づかないバージョン情報やリリース予定などの外部知識は含まれていません。

時間表現の正確性 ✓ PASS

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

「残存していたファイルにも適用」という表現は、PR Descriptionの「remaining ... tests that still used」という記述と合致しており、時間的な文脈が正確に伝わります。