Minitest ライフサイクルの理解で解消した ENV リーク誤検知

rails/rails

PostgresqlDbConsoleTestLeakChecker が環境変数のリークを誤検知していた問題を、#run オーバーライドから setup/teardown への切り替えで修正しました。テストコードのみの変更ですが、Minitest のライフサイクルへの深い理解が求められる修正です。

背景

LeakChecker モジュールが ActiveSupport::TestCaseprepend される形で導入され、テスト間の環境変数リークを検出できるようになりました。しかしその直後から、PostgresqlDbConsoleTest の 4 つのテストが以下のような失敗を報告するようになりました(#56812):

Environment leak detected:
  - Added variables:
    - PGPORT
  - Changed variables:
    - PGUSER from "postgres" to "user"
    - PGHOST from "postgres" to "host"

対象のテストは test_postgresql_fulltest_postgresql_with_ssltest_postgresql_include_passwordtest_postgresql_include_variables の 4 件です。これらのテストは実際には ENV を汚染していませんでした。問題の本質は ENV の復元タイミングにありました。

技術的な変更

旧実装の #run オーバーライドと新実装の setup/teardown では、ENV 復元が行われる Minitest ライフサイクル上の位置が異なります。

旧実装では #run をオーバーライドして preserve_pg_env ヘルパーで super を包み、ensure ブロックで ENV を復元していました。しかし LeakChecker#after_teardownsuper の内部で呼び出されるため、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 の前に確実に実行されることが仕様として保証されています。

LeakCheckerafter_teardown に処理を置くことで、テストコードが teardown で後処理を完了させた後に ENV の状態を検査できる設計になっています。今回の修正は、この設計意図に沿った形でテストコードを書き直したものです。

まとめ

本 PR は、Minitest のライフサイクルにおける #runsetup/teardown の実行順序の差異に起因したテスト失敗を修正したものです。#run オーバーライドの ensure ブロックは after_teardown より後に実行されるという落とし穴を避けるため、ENV の後処理を teardown に移すことで LeakChecker との実行順序を正しく整合させています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
299a04f2

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

「リード文(総論)→背景・技術的な変更・設計判断(各論)→まとめ(結論)」という3部構成が明確に適用されており、非常に分かりやすい構成です。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```ruby:ファイルパス)およびGitHubのIssue/PRリンク記法が正しく使用されています。

対象読者への適合性 ✓ PASS

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

Minitestのライフサイクルや環境変数など、専門知識を持つエンジニアを対象とした適切な技術レベルで書かれています。

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

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

各セクションが総論→各論で構成され、各段落はトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、可読性が高いです。

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロックは、提供されたDiff情報と完全に一致しており、変更点(削除された#run、追加されたsetup/teardown)を正確に反映しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「Minitest ライフサイクル」「prepend」「after_teardown」などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

「#runのensureブロックがafter_teardownより後に実行される」という問題の核心について、PR情報に基づいた技術的に正確な説明がなされています。

事実の突合 ✓ PASS

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

記事内のすべての主張(問題の背景、失敗したテスト名、解決策)が、PRのDescriptionやDiff内容によって裏付けられています。ハルシネーションは検出されませんでした。

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

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

PR番号(#57001)やIssue番号(#56812)などの数値・固有名詞はすべて正確に記載されています。

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

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

タイトル「Minitest ライフサイクルの理解で解消した ENV リーク誤検知」は、PRの主題を的確に表現しつつ、変更の核心である「なぜ」の部分を強調しており、秀逸です。

外部知識の正確性 ✓ PASS

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

記事の内容はPR情報とDiffに限定されており、バージョンサポート状況やリリース日といった検証不可能な外部知識の追加はありません。

時間表現の正確性 ✓ PASS

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

「LeakChecker導入直後から失敗するようになった」などの時間的な前後関係が、PR情報と一致しており、正確に表現されています。