Ruby-on-rails FactoryGirl 和多态关联

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7747945/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 02:09:32  来源:igfitidea点击:

FactoryGirl and polymorphic associations

ruby-on-railsfactory-bot

提问by Feech

The design

该设计

I have a User model that belongs to a profile through a polymorphic association. The reason I chose this design can be found here. To summarize, there are many users of the application that have really different profiles.

我有一个通过多态关联属于配置文件的用户模型。我选择这个设计的原因可以在这里找到。总而言之,该应用程序的许多用户具有非常不同的配置文件。

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

After choosing this design, I'm having a hard time coming up with good tests. Using FactoryGirl and RSpec, I'm not sure how to declare the association the most efficient way.

选择这个设计后,我很难想出好的测试。使用 FactoryGirl 和 RSpec,我不确定如何以最有效的方式声明关联。

First attempt

第一次尝试

factories.rb

工厂.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

用户规范.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

Comments / other thoughts

评论/其他想法

As mentioned in the comments in the user spec, using Artist seems brittle. What if my factories change in the future?

正如用户规范中的评论中提到的,使用 Artist 似乎很脆弱。如果我的工厂将来发生变化怎么办?

Maybe I should use factory_girl callbacksand define an "artist user" and "musician user"? All input is appreciated.

也许我应该使用factory_girl 回调并定义“艺术家用户”和“音乐家用户”?感谢所有输入。

采纳答案by membLoper

Factory_Girl callbacks would make life much easier. How about something like this?

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

回答by veritas1

Although there is an accepted answer, here is some code using the new syntax which worked for me and might be useful to someone else.

虽然有一个公认的答案,但这里有一些使用新语法的代码,它们对我有用,可能对其他人有用。

spec/factories.rb

规格/工厂.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

spec/models/artist_spec.rb

规格/模型/artist_spec.rb

before(:each) do
  @artist = FactoryGirl.create(:artist_user)
end

Which will create the artist instance as well as the user instance. So you can call:

这将创建艺术家实例以及用户实例。所以,你可以拨打:

@artist.profile

to get the Artist instance.

获取艺术家实例。

回答by kuboon

Use traits like this;

使用这样的性状;

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

now you can get user instance by FactoryGirl.create(:user, :artist)

现在您可以通过以下方式获取用户实例 FactoryGirl.create(:user, :artist)

回答by Kingsley Ijomah

You can also solve this using nested factories (inheritance), this way you create a basic factory for each class then nest factories that inherit from this basic parent.

您也可以使用嵌套工厂(继承)来解决这个问题,这样您就可以为每个类创建一个基本工厂,然后嵌套从这个基本父级继承的工厂。

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

You now have access to the nested factories as follows:

您现在可以访问嵌套工厂,如下所示:

artist_profile = create(:artist_profile)
musician_profile = create(:musician_profile)

Hope this helps someone.

希望这可以帮助某人。

回答by Darkside

It seems that polymorphic associations in factories behave the same as regular Rails associations.

工厂中的多态关联似乎与常规 Rails 关联的行为相同。

So there is another less verbose way if you don't care about attributes of model on "belongs_to" association side (User in this example):

因此,如果您不关心“belongs_to”关联方(在本例中为用户)的模型属性,还有另一种不那么冗长的方式:

# 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 associations: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations

FactoryGirl 协会:https: //github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations

回答by vich

I currently use this implementation for dealing with polymorphic associations in FactoryGirl:

我目前使用这个实现来处理多态关联FactoryGirl

In /spec/factories/users.rb:

在 /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

Then, create the records as usual: FactoryGirl.create(:artist_user)

然后,照常创建记录: FactoryGirl.create(:artist_user)