Posted at

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)にしています。