Minitest ライフサイクルの理解で解消した ENV リーク誤検知
PostgresqlDbConsoleTest で LeakChecker が環境変数のリークを誤検知していた問題を、#run オーバーライドから setup/teardown への切り替えで修正しました。テストコードのみの変更ですが、Minitest のライフサイクルへの深い理解が求められる修正です。
背景
LeakChecker モジュールが ActiveSupport::TestCase に prepend される形で導入され、テスト間の環境変数リークを検出できるようになりました。しかしその直後から、PostgresqlDbConsoleTest の 4 つのテストが以下のような失敗を報告するようになりました(#56812):
Environment leak detected:
- Added variables:
- PGPORT
- Changed variables:
- PGUSER from "postgres" to "user"
- PGHOST from "postgres" to "host"
対象のテストは test_postgresql_full、test_postgresql_with_ssl、test_postgresql_include_password、test_postgresql_include_variables の 4 件です。これらのテストは実際には ENV を汚染していませんでした。問題の本質は ENV の復元タイミングにありました。
技術的な変更
旧実装の #run オーバーライドと新実装の setup/teardown では、ENV 復元が行われる Minitest ライフサイクル上の位置が異なります。
旧実装では #run をオーバーライドして preserve_pg_env ヘルパーで super を包み、ensure ブロックで ENV を復元していました。しかし LeakChecker#after_teardown は super の内部で呼び出されるため、ensure による復元よりも先に ENV の検査が実行されていました:
#run -> preserve_pg_env do
super -> before_setup (LeakChecker が ENV をスナップショット)
-> テスト本体 (PGUSER, PGHOST 等を変更)
-> after_teardown (LeakChecker がリークを検知!) ❌
ensure
ENV を復元 (遅すぎる)
新実装では setup/teardown を使用します。これらは LeakChecker#after_teardown よりも前に実行されるライフサイクルフックです:
before_setup (LeakChecker が ENV をスナップショット)
setup (PG 環境変数をバックアップ)
テスト本体 (PGUSER, PGHOST 等を変更)
teardown (PG 環境変数を復元) ✅
after_teardown (LeakChecker は差分なし) ✅
変更前:
def run(*)
preserve_pg_env do
super
end
end
# ...
def preserve_pg_env
old_values = ENV_VARS.map { |var| ENV[var] }
yield
ensure
ENV_VARS.zip(old_values).each { |var, value| ENV[var] = value }
end
変更後:
def setup
super
@_pg_env_backup = ENV_VARS.index_with { |var| ENV[var] }
end
def teardown
ENV_VARS.each { |var| ENV[var] = @_pg_env_backup[var] }
super
end
index_with を用いてキーと値を対応付けた Hash として @_pg_env_backup に保存し、teardown で一括復元する設計に変わっています。不要になった preserve_pg_env プライベートメソッドも合わせて削除されています。
設計判断
#run オーバーライドではなく setup/teardown を使う という方針が採用されました。
#run オーバーライドは Minitest の内部的なエントリポイントを直接制御する強力な手段ですが、before_setup / after_teardown などのフックとの実行順序が直感に反しやすい欠点があります。一方、setup/teardown は Minitest の公式なライフサイクル API であり、after_teardown の前に確実に実行されることが仕様として保証されています。
LeakChecker は after_teardown に処理を置くことで、テストコードが teardown で後処理を完了させた後に ENV の状態を検査できる設計になっています。今回の修正は、この設計意図に沿った形でテストコードを書き直したものです。
まとめ
本 PR は、Minitest のライフサイクルにおける #run と setup/teardown の実行順序の差異に起因したテスト失敗を修正したものです。#run オーバーライドの ensure ブロックは after_teardown より後に実行されるという落とし穴を避けるため、ENV の後処理を teardown に移すことで LeakChecker との実行順序を正しく整合させています。