FactoryGirl и полиморфные ассоциации - PullRequest
65 голосов
/ 13 октября 2011

Дизайн

У меня есть модель User, которая принадлежит профилю через полиморфную ассоциацию. Причину, по которой я выбрал этот дизайн, можно найти здесь . Подводя итог, можно сказать, что многие пользователи приложения имеют действительно разные профили.

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 и определить «пользователь исполнителя» и «пользователь музыканта»? Весь вклад приветствуется.

Ответы [ 6 ]

144 голосов
/ 27 июля 2012

Несмотря на то, что есть принятый ответ, здесь приведен код, использующий новый синтаксис, который работал для меня и может быть полезен для кого-то еще.

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.

35 голосов
/ 02 сентября 2014

Используйте такие черты, как:

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)

10 голосов
/ 13 октября 2011

Обратные вызовы 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
4 голосов
/ 15 июня 2014

Вы также можете решить эту проблему, используя вложенные фабрики (наследование), таким образом, вы создаете базовую фабрику для каждого класса. фабрики гнезд, которые наследуются от этого основного родителя.

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)

Надеюсь, это кому-нибудь поможет.

2 голосов
/ 26 марта 2013

Кажется, что полиморфные ассоциации на фабриках ведут себя так же, как обычные ассоциации Rails.

Таким образом, существует другой, менее многословный способ, если вам не нужны атрибуты модели на стороне ассоциации "own_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

1 голос
/ 10 мая 2017

В настоящее время я использую эту реализацию для работы с полиморфными ассоциациями в 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)

...