tutorial rails factorybot example bot ruby-on-rails factory-bot

ruby on rails - rails - FactoryGirl y asociaciones polimórficas



rspec factory bot (6)

El diseño

Tengo un modelo de usuario que pertenece a un perfil a través de una asociación polimórfica. La razón por la que elegí este diseño se puede encontrar here . En resumen, hay muchos usuarios de la aplicación que tienen perfiles realmente diferentes.

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

Después de elegir este diseño, estoy teniendo dificultades para realizar buenas pruebas. Usando FactoryGirl y RSpec, no estoy seguro de cómo declarar la asociación de la manera más eficiente.

Primer intento

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

Comentarios / otros pensamientos

Como se menciona en los comentarios en la especificación del usuario, usar Artist parece frágil. ¿Qué pasa si mis fábricas cambian en el futuro?

¿Tal vez debería usar las devoluciones de llamada factory_girl y definir un "usuario de artista" y "usuario de músico"? Toda la entrada es apreciada.


Actualmente uso esta implementación para manejar asociaciones polimórficas en FactoryGirl :

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

Luego, cree los registros como de costumbre: FactoryGirl.create(:artist_user)


Aunque hay una respuesta aceptada, aquí hay un código que usa la nueva sintaxis que funcionó para mí y podría ser útil para otra persona.

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

Que creará la instancia del artista así como la instancia del usuario. Entonces puedes llamar:

@artist.profile

para obtener la instancia de artista.


Las devoluciones de llamada Factory_Girl harían la vida mucho más fácil. ¿Qué tal algo como esto?

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


Parece que las asociaciones polimórficas en las fábricas se comportan de la misma manera que las asociaciones regulares de Rails.

Entonces, hay otra manera menos detallada si no te importan los atributos del modelo en el lado de la asociación "belongs_to" (usuario en este ejemplo):

# 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

Asociaciones de FactoryGirl: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations


También puede resolver esto usando fábricas anidadas (herencia), de esta manera puede crear una fábrica básica para cada clase y luego anidar las fábricas que heredan de este principal básico.

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

Ahora tiene acceso a las fábricas anidadas de la siguiente manera:

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

Espero que esto ayude a alguien.


Usa rasgos como este;

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

ahora puede obtener la instancia de usuario de FactoryGirl.create(:user, :artist)