我有一个通过多态关联属于个人资料的用户模型。我选择此设计的原因可以找到here。总而言之,该应用程序的许多用户具有完全不同的配置文件。
class User < ActiveRecord::Base
belongs_to :profile, :dependent => :destroy, :polymorphic => true
end
class Artist < ActiveRecord::Base
has_one :user, :as => :profile
end
class Musician < ActiveRecord::Base
has_one :user, :as => :profile
end
选择这个设计后,我很难做出好的测试。使用FactoryGirl和RSpec,我不确定如何以最有效的方式声明关联。
factories.rb
Factory.define :user do |f|
# ... attributes on the user
# this creates a dependency on the artist factory
f.association :profile, :factory => :artist
end
Factory.define :artist do |a|
# ... attributes for the artist profile
end
user_spec.rb
it "should destroy a users profile when the user is destroyed" do
# using the class Artist seems wrong to me, what if I change my factories?
user = Factory(:user)
profile = user.profile
lambda {
user.destroy
}.should change(Artist, :count).by(-1)
end
正如用户规范中的评论所述,使用Artist似乎很脆弱。如果我的工厂将来改变怎么办?
也许我应该使用factory_girl callbacks并定义“艺术家用户”和“音乐家用户”?所有的意见都表示赞赏。
答案 0 :(得分:143)
虽然有一个公认的答案,但这里有一些使用新语法的代码对我有用,可能对其他人有用。
<强>规格/ factories.rb 强>
FactoryGirl.define do
factory :musical_user, class: "User" do
association :profile, factory: :musician
#attributes for user
end
factory :artist_user, class: "User" do
association :profile, factory: :artist
#attributes for user
end
factory :artist do
#attributes for artist
end
factory :musician do
#attributes for musician
end
end
<强>规格/模型/ artist_spec.rb 强>
before(:each) do
@artist = FactoryGirl.create(:artist_user)
end
这将创建艺术家实例以及用户实例。所以你可以打电话:
@artist.profile
获取Artist实例。
答案 1 :(得分:33)
使用这样的特征;
FactoryGirl.define do
factory :user do
# attributes_for user
trait :artist do
association :profile, factory: :artist
end
trait :musician do
association :profile, factory: :musician
end
end
end
现在您可以通过FactoryGirl.create(:user, :artist)
答案 2 :(得分:10)
Factory_Girl回调会让生活变得更轻松。这样的事情怎么样?
Factory.define :user do |user|
#attributes for user
end
Factory.define :artist do |artist|
#attributes for artist
artist.after_create {|a| Factory(:user, :profile => a)}
end
Factory.define :musician do |musician|
#attributes for musician
musician.after_create {|m| Factory(:user, :profile => m)}
end
答案 3 :(得分:4)
您也可以使用嵌套工厂(继承)解决此问题,这样您就可以为每个类创建基本工厂 嵌套从这个基本父级继承的工厂。
FactoryGirl.define do
factory :user do
# attributes_for user
factory :artist_profile do
association :profile, factory: :artist
end
factory :musician_profile do
association :profile, factory: :musician
end
end
end
您现在可以访问嵌套工厂,如下所示:
artist_profile = create(:artist_profile)
musician_profile = create(:musician_profile)
希望这有助于某人。
答案 4 :(得分:2)
工厂中的多态关联似乎与常规Rails关联的行为相同。
如果你不关心模特的属性,那么还有另一种不那么冗长的方式。#34; belongs_to&#34;关联方(本例中的用户):
# Factories
FactoryGirl.define do
sequence(:email) { Faker::Internet.email }
factory :user do
# you can predefine some user attributes with sequence
email { generate :email }
end
factory :artist do
# define association according to documentation
user
end
end
# Using in specs
describe Artist do
it 'created from factory' do
# its more naturally to starts from "main" Artist model
artist = FactoryGirl.create :artist
artist.user.should be_an(User)
end
end
FactoryGirl协会:https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations
答案 5 :(得分:1)
我目前使用此实现来处理FactoryGirl
中的多态关联:
在/spec/factories/users.rb中:
FactoryGirl.define do
factory :user do
# attributes for user
end
# define your Artist factory elsewhere
factory :artist_user, parent: :user do
profile { create(:artist) }
profile_type 'Artist'
# optionally add attributes specific to Artists
end
# define your Musician factory elsewhere
factory :musician_user, parent: :user do
profile { create(:musician) }
profile_type 'Musician'
# optionally add attributes specific to Musicians
end
end
然后,照常创建记录:FactoryGirl.create(:artist_user)