Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails: テーブル名をカスタマイズするとfixture読み込みが失敗する問題を修正する(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

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).

しかし、これは他のモデルでは発生しません。一体どうなっているのでしょうか?

🔗 実際に起きていること

このとき何が起きているのかを以下に示します。

  1. Rails内部では、activerecord/lib/active_record/fixtures.rbYAMLファイル名を定数化(constantize)する形でモデル名の推測を試みますが、私の場合に生成されるBuildProgramはクラス名(Build::Program)と合っていません。

  2. その結果、model_classnilに設定されてしまいます。

  3. model_classnilになっているので、その後のactiverecord/lib/active_record/fixture_set/table_row.rbfixtureに期待されるあらゆるものが失敗しますが、エラーを何も出力しません。

  4. fixtureで期待される操作には、クラスのtimestamps属性も含まれています。

私だったらの話ですが、こういう場合はエラーとして扱うでしょう。fixtureファイルがテーブルに読み込まれ、それがどのクラスに対応するのかをフレームワーク側で判断できなかったら、デフォルトでエラー扱いするのが筋でしょう。

しかしこれは私の責務ではなかったので、エラーを何も表示せずに失敗しました。
そういうわけで、この問題をググると、以下のような「テーブル名の活用形が見つからない」「テーブル名が不規則活用」という見当違いの結果ばかりが表示されています。

参考: ruby on rails - ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR: null value in column "created_at" violates not-null constraint - Stack Overflow

🔗 修正方法

それでは修正方法を紹介しましょう。ご存じないかもしれませんが、実は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されるようになります。

これで、テストを再実行すればきれいに動くはずです。

関連記事

Rails 7でSMTPメールをAWS SESで送信する正しい方法(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。