下記のような UserGroup, User, Blog があるとします。
# == Schema Information
#
# Table name: user_groups
#
# id :integer not null, primary key
# name :string(255)
# created_at :datetime not null
# updated_at :datetime not null
#
class UserGroup < ActiveRecord::Base
end
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# user_group_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class User < ActiveRecord::Base
has_many :blogs
belongs_to :user_group
end
# == Schema Information
#
# Table name: blogs
#
# id :integer not null, primary key
# name :string(255)
# user_id :integer
# user_group :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Blog < ActiveRecord::Base
belongs_to :user
belongs_to :user_group
end
これらの factory_girl の定義を書いてみましょう。素直に書くと下記のようになりました。
FactoryGirl.define do
factory :user_group do
sequence(:name) { "グループ#{i}"}
end
end
FactoryGirl.define do
factory :user do
sequence(:name) { |i| "ユーザ#{i}" }
user_group
end
end
FactoryGirl.define do
factory :blog do
sequence(:name) { |i| "ブログ#{i}" }
user_group
user
end
end
この状態でFactoryGirl.create(:blog)
のようにすると、UserGroup と User のレコードも一緒に作成されます。これは便利なのですが、上記の定義だと Blog の作成をする時と User の作成をする時で二つの UserGroup が作成されてしまいます。これはよくありませんね。BlogとUserは同じUserGroupを参照しているのが望ましいです。
下記のように書ければよいのですが、これでFactoryGirl.create(:blog)
とすると FactoryGirl::AttributeDefinitionError: Attribute already defined: user_group
となってしまいます。現状のFactoryGirlでは関連の属性を動的に設定することは出来ないようです(静的な値であれば設定できます)。
FactoryGirl.define do
factory :blog do
sequence(:name) { |i| "ブログ#{i}" }
user_group
association :user, factory: user, user_group: user_group
end
end
そこで下記のようにして対応します。
FactoryGirl.define do
factory :blog do
sequence(:name) { |i| "ブログ#{i}" }
user_group
user { create(:user, user_group: user_group) }
end
end
これでFactoryGirl.create(:blog)
としたときに UserGroup が作られるのは一度だけになりました。ただし、この書き方をすると、FactoryGirl.build(:blog)
としたときにも User と UserGroup が作られてしまいます。FactoryGirl.build
も使いたいときには build 用の定義を別途作ってあげる必要がありそうですね。
factory_girl を初めとした Fixture Replacement は関連が複雑なときは定義がけっこう大変な印象です。とはいえ今更Fixturesには戻れないのですが…。
参考
ruby on rails - Pass parameter in setting attribute on association in FactoryGirl - Stack Overflow