LoginSignup
4
4

More than 3 years have passed since last update.

【FactoryBot】外部参照キーのカラムデータの作成方法

Last updated at Posted at 2020-05-14

【FactoryBot】外部参照キーのカラムデータの作成方法

FactoryBotで外部参照キーとなるカラムデータを作成する方法を調べたので内容をまとめます.
例えばPostモデルでファクトリデータを作成する際に一緒に投稿者となるUserのファクトリデータを作成するような状況です。

目次

  1. 状況
  2. 動作環境
  3. 手順
  4. 結果
  5. 当初つまづいた点
  6. おわりに

状況

Messageモデルのテストを行うためにConversationモデルとUserモデルのデータが必要です。
下記が今回のER図です。

Messageモデルはbelongs_to :user, belongs_to :conversationというアソシエーションを持ちます。

すなわちMessageモデルのFactoryBotを実行した際にConversationモデルとUserモデルのデータを生成する必要があります。

image.png

message.rb
class Message < ApplicationRecord
  belongs_to :conversation
  belongs_to :user
end
user.rb
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
conversation.rb
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を定義

users.rb
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を利用するのか明示しています

conversations.rb
FactoryBot.define do
  factory :conversation do

  association :sender, factory: :user
  association :recipient, factory: :user

  end
end

実際にConversaionモデルのFactoryBotを動かしてみました。
conversation.senderとconversation.recipientにFactoryBot :userで作成されたデータが入っています。

image.png

STEP 3. MessageモデルのFactoryBotを定義

messages.rb
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

結果

message_spec.rb
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している。

image.png

当初つまづいたところ

当初はSTEP 3. FactoryBot:messageのコードでblankエラーが発生していた

image.png

原因

そこでMessagemモデルのファイルを確認するとuser_idconversation_idにnullチェックがあったためBlankエラーになっていた。

image.png

つまりこのようなバリデーションがある場合はcreateメソッドを利用する必要があります。

messages.rb
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できる.

image.png

おわりに

今回の件で以下のことを学びました。
1. associationメソッドを利用することで外部参照キーのデータを作成できる
2. associationメソッドはbuildのため、idのnullチェックがある場合はcreateを利用する

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4