J'ai un modèle utilisateur qui appartient à un profil via une association polymorphe. La raison pour laquelle j'ai choisi cette conception peut être trouvée ici . Pour résumer, de nombreux utilisateurs de l'application ont des profils vraiment différents.
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
Après avoir choisi ce design, j'ai du mal à trouver de bons tests. En utilisant FactoryGirl et RSpec, je ne sais pas comment déclarer l'association de la manière la plus efficace.
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
Comme mentionné dans les commentaires dans les spécifications utilisateur, l'utilisation d'Artist semble fragile. Et si mes usines changent à l'avenir?
Peut-être devrais-je utiliser callbacks factory_girl et définir un "utilisateur artiste" et "utilisateur musicien"? Toutes les contributions sont appréciées.
Les rappels Factory_Girl rendraient la vie beaucoup plus facile. Que diriez-vous quelque chose comme ça?
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
Bien qu'il existe une réponse acceptée, voici du code utilisant la nouvelle syntaxe qui a fonctionné pour moi et pourrait être utile à quelqu'un d'autre.
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
Ce qui créera l'instance d'artiste ainsi que l'instance d'utilisateur. Vous pouvez donc appeler:
@artist.profile
pour obtenir l'instance Artist.
Utilisez des traits comme celui-ci;
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
vous pouvez maintenant obtenir l'instance utilisateur par FactoryGirl.create(:user, :artist)
Vous pouvez également résoudre ce problème en utilisant des usines imbriquées (héritage), de cette façon, vous créez une usine de base pour chaque classe, puis imbriquez des usines qui héritent de ce parent de base.
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
Vous avez maintenant accès aux usines imbriquées comme suit:
artist_profile = create(:artist_profile)
musician_profile = create(:musician_profile)
J'espère que cela aide quelqu'un.
Il semble que les associations polymorphes dans les usines se comportent de la même manière que les associations Rails.
Il y a donc une autre manière moins verbeuse si vous ne vous souciez pas des attributs du modèle du côté de l'association "appartient à" (l'utilisateur dans cet exemple):
# 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
Associations FactoryGirl: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations
J'utilise actuellement cette implémentation pour gérer les associations polymorphes dans FactoryGirl
:
Dans /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
Ensuite, créez les enregistrements comme d'habitude: FactoryGirl.create(:artist_user)