`NotificationAssertions` ヘルパーによるActiveRecordテストの簡素化
ActiveSupport::Notifications.subscribe と手動フラグ管理によるテストパターンを、assert_notification ヘルパーで置き換えることで、ActiveRecordの通知テストが大幅に簡潔になりました。
背景
#56989 で開始されたActiveRecordテストの NotificationAssertions 対応を、残存していたファイルにも適用したのが本PRです。対象は trilogy_adapter_test.rb、instrumentation_test.rb、query_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 の手動購読パターンが一掃され、テストの可読性と安全性が向上しています。