Help us understand the problem. What is going on with this article?

FactoryGirlで「多対多」や「複数の1対多」のアソシエーションを設定する

More than 1 year has passed since last update.

FactoryGirlでインスタンスの作成時にアソシエーションを設定する方法は色々ありそうですが、一番簡単だと思った方法を書いておきます。

前提条件

バージョン

  • Rails 5.0.2
  • Rspec 3.5.4
  • factory_girl_rails > 4.4.1

アソシエーション

今回設定するアソシエーションは以下の通り。

user.rb
class User < ApplicationRecord
  has_many :groups, through: :group_users
  has_many :group_users
  has_many :messages
end
group.rb
class Group < ApplicationRecord
  has_many :group_users
  has_many :users, through: :group_users
  has_many :messages
end
group_user.rb
class GroupUser < ApplicationRecord
  belongs_to :group
  belongs_to :user
end
message.rb
class Message < ApplicationRecord
  belongs_to :group
  belongs_to :user
end

userとgroupがgroup_userを介して多対多の関係、
user,groupはそれぞれmessageと1対多の関係です。

factory_girlの記法の省略

spec/rails_helper.rbに下記を追加し、インスタンス作成時にFactoryGirlという記述を省略しています。
rails_helper.rb
config.include FactoryGirl::Syntax::Methods

「1対多(has_many)」の設定

「多対多」の設定をする前に「1対多」の場合のhas_manyの設定をみておきます。
例えば、user has_many messagesの場合、userインスタンス作成時にafter(:create)を用いて関連したmessagesを作成することができます。

user.rb
FactoryGirl.define do

  factory :user do
    name                    "komekami"
    email                   "komekami@mail"
    password                "12345678"
    password_confirmation   "12345678"

    after(:create) do |user|
      create_list(:message, 3, user: user)
    end
  end
end

これでuser_idに作成されたuserインスタンスのidを持つmessageインスタンスが3つ作成できます。create_listは第一引数に作成したいインスタンス名、第二引数に作成したい個数、それ以降に「カラム名: 値」の形で値の上書きができるので、作成したuserのidをmessageのuser_idカラムに上書きしますよー、ってことですね。

「多対多」の設定

続いて「多対多」の設定です。
例えば、user has_many groupsの場合、userインスタンス作成時に関連したgroupsインスタンスを作成します。

user.rb
FactoryGirl.define do

  factory :user do
    name                    "komekami"
    email                   "komekami@mail"
    password                "12345678"
    password_confirmation   "12345678"

    after(:create) do |user|
      create_list(:group, 3, users: [user])
    end
  end
end

「1対多」のhas_manyの場合とほとんど同じです。create_listのuserの受け渡しが複数形と配列の形になっているのがポイント。user_ids[]に入るからですね。中間テーブル(group_users)を用いずに設定できました、すっきり。

「複数の1対多」の場合

これでuserとgroupの多対多の関係が設定できました。しかし、今回はさらに、userとgroupがそれぞれ多数のmessagesを持っている、という関係があります。これをafter(:create)で全て紐付けようとすると、少し複雑そうな気がします。
そこで、Rspec側で値をセットするやり方を考えます。

messages_controller_spec.rb
  let(:user) { create(:user) }
  let(:message) { create(:message, user: user, group: user.groups.first) }

例えば、messages_controllerのテストの際、let(:user)を使ってuserを定義し、前述したafter(:create)で同時にgroupを作成します。その後、let(:message)でmessageを定義する際に、createの引数にuserとgroupを記述することにより、let(:user)が読み込まれて上書きされるようになります。

message.rb
FactoryGirl.define do

  factory :message do
    body      "hello world!"
    image     "sample.jpg"
    user
    group
  end
end

messageのFactoryGirlにはuserとgroupを置いておけばよいですね。

以上で、アソシエーションの設定ができました。
ご指摘等ありましたら、コメントお願いします!

参考にしたサイト
factory_girl/GETTING_STARTED.md
FactoryGirlで関連データを同時に生成する方法いろいろ
factory_girl で最低限知っておきたい4つの使い方

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした