RedisCacheStore のテストで抑制エラーを再スローし可視化
RedisCacheStore のテストが、Redis のタイムアウトで発生する RedisClient::ConnectionError を内部で抑制し、nil を返すだけで失敗原因が不明瞭になる問題を解決します。エラーをそのまま再スローさせることで、テスト実行時に根本原因が明示されます。
背景
RedisCacheStoreCommonBehaviorTest は pool: false と timeout: 0.1 の設定で Redis に接続し、スローレス環境(例: Ruby trunk debug ビルド)で接続や読み込みがタイムアウトすると RedisClient::ConnectionError が発生します。RedisCacheStore#failsafe はこの例外を捕捉して nil を返すため、テストは Expected nil to be truthy. という抽象的な失敗メッセージだけを示します。
この抑制された例外はログにも出力されず、テスト実行者はタイムアウトが原因かどうかを推測せざるを得ませんでした。結果として、原因特定に余計なデバッグ作業が必要となっていました。
このため、テスト失敗時に実際の例外情報を取得できるようにすることが求められました。
技術的な変更
テストクラス RedisCacheStoreCommonBehaviorTest に lookup_store メソッドをオーバーライドし、error_handler オプションに例外を再スローするラムダ ->(method:, returning:, exception:) { raise exception } を注入しました。これにより、failsafe が例外を捕捉しても上位に伝搬し、スタックトレースがそのまま表示されます。
@@ -326,6 +326,10 @@
def capture_redis_commands(&block)
capture_notifications("redis_query.active_support_test", &block).flat_map { |e| e.payload.fetch(:commands) }
end
+ def lookup_store(options = {})
+ super(options.merge(error_handler: ->(method:, returning:, exception:) { raise exception }))
+ end
end
class RedisCacheStoreWithDistributedRedisTest < RedisCacheStoreCommonBehaviorTest
このオーバーライドはテストクラス内部に限定されているため、FailureSafety や FailureRaising といった他のテストは従来通り例外抑制の動作を保持します。失敗したテストは次のようにエラー情報が直接出力され、タイムアウトが原因であることが一目で分かります。
設計判断
本変更は テスト専用のエラーハンドラ注入 という設計方針を採用しています。実装コードや本番環境の RedisCacheStore#failsafe ロジックは一切変更せず、テスト時にだけ挙動を変えることで安全性を確保しました。
テスト対象のストア生成時に error_handler を差し込むだけで例外の振る舞いを切り替えられる点は、テストの可視性向上と本番コードへの影響回避という二つの要件を同時に満たす合理的な選択です。変更範囲が限定的であるため、リグレッションリスクも最小限に抑えられました。
このアプローチは、エラーハンドリングのポリシーを環境ごとに柔軟に切り替えられる設計の一例として参考になります。
まとめ
RedisCacheStoreCommonBehaviorTest に error_handler を注入し、抑制されていた RedisClient 系エラーを再スローさせることで、テスト失敗時に直接的な例外情報が得られるようになりました。テストコードのみを修正し、実装ロジックや本番挙動への影響を排除した安全な設計判断です。