ミュータブルな定数の拡張性をテストで保証する
#57323 で導入された定数のフリーズ対応により、フレームワーク利用者が拡張することを想定した定数まで誤って凍結されるケースが発生していました。本PRはその再発防止として、拡張可能であるべき定数に対してテストを追加します。
背景
Ractorセーフ化を目的とした Style/MutableConstant copの有効化(#57323)は、リテラルで定義された定数を一括でフリーズするものでした。しかしこの変更の直後、フレームワーク利用者による拡張を想定した定数まで凍結されてしまう問題が2件報告されています。
具体的には、#57332 が Date::DATE_FORMATS の意図しないフリーズを修正し、#57333 が AbstractMysqlAdapter::NATIVE_DATABASE_TYPES のフリーズを修正しています。この定数はフレームワーク利用者がカスタム型を登録するために変更することが想定されており、フリーズにより FrozenError が発生していました。これらの修正はいずれも、定数が拡張可能であることを保証するテストを持たずに行われていました。
このような「ドキュメントや慣習上は拡張可能とされているが、テストで保証されていない定数」の存在が、フリーズ対応のリグレッションを引き起こした根本原因です。
技術的な変更
本PRは2つのテストファイルにそれぞれ1つずつテストメソッドを追加し、対象の定数が変更可能であることを実行時に検証します。
activesupport/test/core_ext/date_ext_test.rb には test_date_formats_can_be_extended が追加されました。カスタムフォーマットキー :custom_date_format を Date::DATE_FORMATS に登録し、to_fs が期待通りの文字列を返すことを検証します。
def test_date_formats_can_be_extended
Date::DATE_FORMATS[:custom_date_format] = "%B %Y"
assert_equal "February 2005", Date.new(2005, 2, 21).to_fs(:custom_date_format)
ensure
Date::DATE_FORMATS.delete(:custom_date_format)
end
activerecord/test/cases/adapters/abstract_mysql_adapter/sql_types_test.rb には test_native_database_types_can_be_extended が追加されました。:custom_mysql_type を AbstractMysqlAdapter::NATIVE_DATABASE_TYPES に登録し、type_to_sql が正しいSQL型名を返すことを確認します。
def test_native_database_types_can_be_extended
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::NATIVE_DATABASE_TYPES[:custom_mysql_type] = { name: "mediumtext" }
assert_equal "mediumtext", type_to_sql(:custom_mysql_type)
ensure
ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::NATIVE_DATABASE_TYPES.delete(:custom_mysql_type)
end
どちらのテストも ensure ブロックで登録したキーを削除しており、テスト間の副作用を防ぐクリーンアップが徹底されています。
設計判断
「変更可能であることの保証」をテストとして明文化するアプローチが採用されました。
#57323 は拡張用途の定数に rubocop:disable コメントを付ける形で例外を管理していますが、コメントだけではそれが実際に機能していることの実行時保証にはなりません。本PRはテストを追加することで、将来の変更(誤ったフリーズ、構造変更など)がCIで即座に検出できる状態を作っています。ensure によるクリーンアップを含む設計は、テスト自体が他のテストに干渉しないことも保証しており、テストの独立性を維持しています。
まとめ
本PRは新機能の追加ではなく、既存の拡張ポイントを「壊せないようにする」ためのテストを加える変更です。定数のフリーズという広範な自動化の中で見落とされやすい「拡張のための可変性」をテストとして表明することで、今後の同種のリグレッションを未然に防ぐ安全網を構築しています。