Rails: テーブル名をカスタマイズするとfixture読み込みが失敗する問題を修正する(翻訳)
Railsのモデルでtable_name
をカスタム定義すると、おそらくテスト用データベースにfixtureを読み込んだときに困ったことになるでしょう。ググって本記事を見つけた方へ: このページで合っています!
私の場合は以下のようなモデルでこの問題を踏みました。
# app/models/build/program.rb
module Build
class Program < ApplicationRecord
self.table_name = "build_programs"
end
end
このモデルに対応するfixtureファイルは以下です。
# test/fixtures/build_programs.yml
first:
started_on: 2018-01-01
workout_a_summary: "Squat, Bench, Row"
workout_b_summary: "Deadlift, Press, Chinup"
workout_c_summary: "Overhead Squat, Incline Bench, Pullup"
デフォルトのtest_helper.rb
ファイルを使っていれば、テスト実行前にこのfixtureがオートロードされます。
# test/test_helper.rb
module ActiveSupport
class TestCase
# test/fixtures/*.ymlにあるすべてのfixtureが
# すべてのテストでABC順に実行される
fixtures :all
end
end
ここで私のように、マイグレーションでこのようなモデルにt.timestamps
を指定すると(なおRails 5.0以降はデフォルトでnon-nullableになります)、テストを実行したときに痛い目に遭います。
ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR: null value in column "created_at" of relation "build_programs" violates not-null constraint
DETAIL: Failing row contains (309456474, 2018-01-01, Squat, Bench, Row, Deadlift, Press, Chinup, Overhead Squat, Incline Bench, Pullup, null, null).
しかし、これは他のモデルでは発生しません。一体どうなっているのでしょうか?
🔗 実際に起きていること
このとき何が起きているのかを以下に示します。
- Rails内部では、
activerecord/lib/active_record/fixtures.rb
がYAMLファイル名を定数化(constantize)する形でモデル名の推測を試みますが、私の場合に生成されるBuildProgram
はクラス名(Build::Program
)と合っていません。 -
その結果、
model_class
はnil
に設定されてしまいます。 -
model_class
がnil
になっているので、その後のactiverecord/lib/active_record/fixture_set/table_row.rb
でfixtureに期待されるあらゆるものが失敗しますが、エラーを何も出力しません。 -
fixtureで期待される操作には、クラスのtimestamps属性も含まれています。
私だったらの話ですが、こういう場合はエラーとして扱うでしょう。fixtureファイルがテーブルに読み込まれ、それがどのクラスに対応するのかをフレームワーク側で判断できなかったら、デフォルトでエラー扱いするのが筋でしょう。
しかしこれは私の責務ではなかったので、エラーを何も表示せずに失敗しました。
そういうわけで、この問題をググると、以下のような「テーブル名の活用形が見つからない」「テーブル名が不規則活用」という見当違いの結果ばかりが表示されています。
🔗 修正方法
それでは修正方法を紹介しましょう。ご存じないかもしれませんが、実はRailsのfixtureには_fixture
という隠れたキーが使えます。このキーは、そのfixtureのクラス定数(クラス名)が何であるかを指示するのに使います。
# test/fixtures/build_programs.yml
_fixture:
model_class: Build::Program
first:
started_on: 2018-01-01
workout_a_summary: "Squat, Bench, Row"
workout_b_summary: "Deadlift, Press, Chinup"
workout_c_summary: "Overhead Squat, Incline Bench, Pullup"
このキーを設定すれば、Railsがfixtureファイル名から誤ったクラス名を推測しなくなり、model_class
に設定した値がめでたくcontstantize
されるようになります。
これで、テストを再実行すればきれいに動くはずです。
概要
原著者の許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。