web-dev-qa-db-ja.com

FactoryGirlとポリモーフィックな関連付け

デザイン

多態的な関連付けを介してプロファイルに属するユーザーモデルがあります。このデザインを選んだ理由は こちら にあります。要約すると、アプリケーションのユーザーは本当に異なるプロファイルを持っています。

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 を使用して、「アーティストユーザー」と「ミュージシャンユーザー」を定義する必要がありますか?すべての入力を歓迎します。

66
Feech

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
11
membLoper

受け入れられた答えがありますが、ここに私のために働いたし、他の誰かに役立つかもしれない新しい構文を使用したコードがあります。

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

spec/models/artist_spec.rb

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

これにより、アーティストインスタンスとユーザーインスタンスが作成されます。だからあなたは電話することができます:

@artist.profile

artistインスタンスを取得します。

145
veritas1

このような特性を使用してください。

_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)でユーザーインスタンスを取得できるようになりました

36
kuboon

ネストされたファクトリ(継承)を使用してこれを解決することもできます。この方法では、各クラスの基本ファクトリを作成し、この基本親から継承するファクトリをネストします。

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)

これが誰かを助けることを願っています。

5
Kingsley Ijomah

工場内のポリモーフィックな関連付けは、通常のRails=関連付けと同じように動作するようです。

したがって、「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アソシエーション: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations

2
Darkside

現在、この実装を使用して、FactoryGirlのポリモーフィックな関連付けを処理しています。

In /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)

1
vich