Rails
RSpec

FactoryBotでbelongs_toとhas_manyの関係にあるやつ

belongs_toとhas_manyの関係にあるモデルオブジェクトをFactoryBotで作っていて、無限ループになってハマった話と対策。

モデル例

Groupに複数のUserが所属するものとする。モデルはこんな感じになる。

class Group < ApplicationRecord
  has_many :users
end

class User < ApplicationRecord
  belongs_to :group
end

これらをテストする時、FactoryBot.create(:user)とすれば、belongs_toなgroupも自動生成され、FactoryBot.create(:group)した時には、has_manyなuserもが自動生成されるになっているとテスト書くのが楽です。

belongs_to宣言しているモデルUserのFactoryBot

以下のようにbelongs_toの宣言そのまま書くだけでいい。

FactoryBot.define do
  factory :user do
    group
  end
end

これでFactoryBot.create(:user)するとGroupのFactoryBotでGroupを作りUserとひもづけてくれる。

has_many宣言しているモデルGroupのFactoryBot

以下のようにするのが望ましいと思われる。

FactoryBot.define do
  factory :group do
    users {[
     FactoryBot.build(:user, group: nil)
    ]}
  end
end

ポイントは

  • FactoryBot.createでなくbuildであること
  • groupにnilを入れていること

である。

ここを、FactoryBot.create(:user)にしてしまうと、

  • UserのFactoryBotもGroupを生成するようになっている
  • つまりGroupのFactoryBotが呼び出される
  • GroupのFactoryBotもUserを生成しようとする
  • つまりUserのFactoryBotが呼び出される
  • 最初に戻る

という無限ループになります。

よって、

  • FactoryBot.build(:user)を使ってDBへの生成は行わずオンメモリで作る
  • さらにgroupをnilを渡す。これによりGroup自身がDBにINSERTされ、Groupのidが決定した後に、そのidがusersのgroup_idに入る

となるように、FactoryBot.build(:user, group: nil)にしています。