【FactoryBot】外部参照キーのカラムデータの作成方法
FactoryBotで外部参照キーとなるカラムデータを作成する方法を調べたので内容をまとめます.
例えばPostモデルでファクトリデータを作成する際に一緒に投稿者となるUserのファクトリデータを作成するような状況です。
目次
状況
Messageモデルのテストを行うためにConversationモデルとUserモデルのデータが必要です。
下記が今回のER図です。
Messageモデルはbelongs_to :user, belongs_to :conversation
というアソシエーションを持ちます。
すなわちMessageモデルのFactoryBotを実行した際にConversationモデルとUserモデルのデータを生成する必要があります。
class Message < ApplicationRecord
belongs_to :conversation
belongs_to :user
end
class User < ApplicationRecord
has_many :senders, class_name: 'Conversation', foreign_key: :sender_id
has_many :recipients, class_name: 'Conversation', foreign_key: :recipient_id
has_many :messages, dependent: :destroy
end
class Conversation < ApplicationRecord
belongs_to :sender, class_name: 'User', foreign_key: :sender_id
belongs_to :recipient, class_name: 'User', foreign_key: :recipient_id
has_many :messages, dependent: :destroy
end
動作環境
OS : macOS Mojave 10.14.6
ruby : 2.6.3p62
rails : 5.2.4
factory_bot_rails : 5.2.0
手順
イメージとして以下の流れを実現するようにします。
STEP1. UserのFactoryBot呼び出し
STEP2. ConversationのFactoryBot呼び出し
STEP3. MessageのFactoryBot呼び出し
STEP 1. UserモデルのFactoryBotを定義
FactoryBot.define do
#名前が重複しないようにシーケンスを利用
factory :user do
sequence :name do |n|
"user_#{n}"
end
end
end
STEP 2. ConversationモデルのFactoryBotを定義
associationを記載することでUserのFactoryBotが呼び出されsenderに代入されます.
ポイント association(senderやrecipient)
とuser
は異なる名前のためfactory: :user
でどのfactoryを利用するのか明示しています
FactoryBot.define do
factory :conversation do
association :sender, factory: :user
association :recipient, factory: :user
end
end
実際にConversaionモデルのFactoryBotを動かしてみました。
conversation.senderとconversation.recipientにFactoryBot :userで作成されたデータが入っています。
STEP 3. MessageモデルのFactoryBotを定義
FactoryBot.define do
factory :message do
sequence :body do |n|
"message_#{n}"
end
#アソシエーション名とファクトリー名が異なる場合はconv factory: :conversationのように
#記述.今回はアソシエーション名とファクトリ名が同じためconversationのみで問題ない.
conversation
#conversationファクトリで作成したuserデータをmessage.userに代入
user { conversation.sender }
end
end
結果
require 'rails_helper'
RSpec.describe Message, type: :model do
it 'バリデーションが通る' do
message = build(:message, body:'test message' )
binding.pry
expect(message).to be_valid
end
end
message内を確認するとuserとconversationにデータが入っており、
テストをPassしている。
当初つまづいたところ
当初はSTEP 3. FactoryBot:messageのコードでblankエラーが発生していた
原因
そこでMessagemモデルのファイルを確認するとuser_id
とconversation_id
にnullチェックがあったためBlankエラーになっていた。
つまりこのようなバリデーションがある場合はcreateメソッドを利用する必要があります。
FactoryBot.define do
factory :message do
sequence :body do |n|
"message_#{n}"
end
after(:build) do |message|
message.conversation = create(:conversation)
message.user = message.conversation.sender
end
end
end
インスタンスが保存されるためidが表示される. その結果,nullチェックのバリデーションもPassできる.
おわりに
今回の件で以下のことを学びました。
- associationメソッドを利用することで外部参照キーのデータを作成できる
- associationメソッドはbuildのため、idのnullチェックがある場合はcreateを利用する