ActiveHashのデータをFactoryBotで使う方法
アクティブハッシュのデータをFactoryBotで使うのにてこずったので、記録に残しておこうかと思います。
事の発端
itemモデルの単体テストコードで使うため、以下のようなFactoryBotを記述しました。
categoryはアクティブハッシュで、itemテーブルと紐づいています。
FactoryBot.define do factory :item do #(略) category_id { 2 } end end
ところがメンターさんに、category_idに直接数値を代入するのではなく、アソシエーションを記述するようにと言われました。
FactoryBotでアソシエーションを記述する方法…。テーブル同士ならこれでOKです。
factory :item do association :user end
categoryについても同じように書いてみました。
factory :item do association :category end
しかしこれではcategory_idは生成されず。どうしたものか。
アクティブハッシュとテーブルのアソシエーション
CategoryモデルとItemモデルはこのようになっていました。
class Category < ActiveHash::Base self.data = [ { id: 1, name: '--' }, { id: 2, name: 'メンズ' } # 略 ] end
class Item < ApplicationRecord extend ActiveHash::Associations::ActiveRecordExtensions belongs_to_active_hash :category #カテゴリーの選択が「--」の時は保存できないようにする validates :category_id, numericality: { other_than: 1 } end
Itemモデルの中ではbelongs_to_active_hash
メソッドでアソシエーションを設定しているんですけど、Categoryモデルにアソシエーションの記述はないんですよね。おそらくそのへんが関係しているのではないかと(細かいことはわからないですが…)。
Categoryモデルをこんな感じに修正しました。
class Category < ActiveHash::Base include ActiveHash::Associations self.data = [ { id: 1, name: '--' }, { id: 2, name: 'メンズ' } # 略 ] has_many :items end
そしてFactoryBotはこのように記述します。
factory :item do category { Category.all.sample } end
これでできました!なるほど、categoryの中からサンプルを一つ取ってくる感じですかね。
ちなみに以下のように書いてはいけないらしいです。
factory :item do association :category end
こうしてしまうと、categoryの新しいレコード(idは最新でnameは空)が生成されてしまいます。
新たな問題発生
解決したかと思いきや。この方法でダミーデータを作ると、category_idが1になることがあるんですよね。category_id: 1
では保存できないので、テストに失敗します。さて困った。
解決策
category_id: 1
でも保存できるように、categoryの一つ目に入れていた「 -- 」を消しました。
self.data = [ { id: 1, name: 'メンズ' }, { id: 2, name: 'レディース' } # 略 ]
そして、Itemモデルの「category_idが1以外」というバリデーションを削除して、FactoryBotの問題は解決です。
その代わり、実際にwebページで入力する際に初めから一つ目の要素が選択された状態になるという問題が生じます。
そこでビューファイルでカテゴリーを選択して入力する部分に、include_blank: true
を追加しました。
<%= f.collection_select(:category_id, Category.all, :id, :name, {include_blank: true}, {class: "select-box"}) %>
これで、ユーザーが選択するまではcategoryは空欄になります。今度こそ解決。
参考
こちらを参考にさせていただきました。