FactoryBotで中間テーブルのテストデータを用意する方法

FactoryBotでテストデータの中間テーブルを用意する方法がわからず、少し詰まったのでメモ。 f:id:t1gerk1ngd0m:20190824230419p:plain

想定

想定としてUserモデルとGroupモデルが存在し、n対nの関係であるとする。 中間テーブルのモデル名はGroup_userとする。

app/models/user.rb

class User < ApplicationRecord
  has_many :group_users, dependent: :destroy
  has_many :groups, through: :group_users
end

app/models/group.rb

class Group < ApplicationRecord
  has_many :group_users, dependent: :destroy
  has_many :users, through: :group_users
end

app/models/group_user.rb

class GroupUser < ApplicationRecord
  belongs_to :group
  belongs_to :user
end

FactoryBotの記述方法

レコードの関連付けを行う場合は色々な書き方があるが、ここではその一例を紹介する。 基本的には関連付けはtraitとcallbackを用いて記述する。

spec/factories/user.rb

FactoryBot.define do
  factory :user do
    sequence(:name) { |n| "test_user#{n}" }
    sequence(:email) { |n| "test_email#{n}@example.com" }
    sequence(:password) { "password" }

    trait :user_with_groups do
      after(:build) do |user|  # after(:build)とした場合、createの場合もcallbackが走る
        user.groups << build(:group)
      end
    end
  end
end

spec/factories/group.rb

FactoryBot.define do
  factory :group do

    equence(:name) { |n| "test_user#{n}" }

  end
end

上記の書き方をしておくと、userデータを作成後、traitでuserに紐づくgroupの作成をするかしないかを切り替える事が出来る。

# Userに紐づくGroupを作成する場合
user = create(:user, :user_with_groups)

# Use単体を作成する場合
user = create(:user)

これでUserに紐づくGroupの作成・非作成を切り替える事が出来た。