2013-04-30 14 views
22

Questo è probabilmente semplice ma non riesco a trovare un esempio da nessuna parte.FactoryGirl sovrascrive l'attributo dell'oggetto associato

I due fabbriche:

FactoryGirl.define do 
    factory :profile do 
    user 

    title "director" 
    bio "I am very good at things" 
    linked_in "http://my.linkedin.profile.com" 
    website "www.mysite.com" 
    city "London" 
    end 
end 

FactoryGirl.define do 
    factory :user do |u| 
    u.first_name {Faker::Name.first_name} 
    u.last_name {Faker::Name.last_name} 

    company 'National Stock Exchange' 
    u.email {Faker::Internet.email} 
    end 
end 

Quello che voglio fare è ignorare alcune delle attributi utente quando creo un profilo:

p = FactoryGirl.create(:profile, user: {email: "[email protected]"}) 

o qualcosa di simile, ma non posso ottieni la sintassi giusta. Errore:

ActiveRecord::AssociationTypeMismatch: User(#70239688060520) expected, got Hash(#70239631338900) 

so che posso farlo creando l'utente prima e poi associarlo con il profilo, ma ho pensato che ci deve essere un modo migliore.

O questo funzionerà:

p = FactoryGirl.create(:profile, user: FactoryGirl.create(:user, email: "[email protected]")) 

ma questo sembra troppo complessa. Non c'è un modo più semplice per sostituire un attributo associato? Qual è la sintassi corretta per questo ??

risposta

6

Penso che si possa fare questo lavoro con i callback e gli attributi transitori. Se si modifica il tuo profilo fabbrica in questo modo:

FactoryGirl.define do 
    factory :profile do 
    user 

    ignore do 
     user_email nil # by default, we'll use the value from the user factory 
    end 

    title "director" 
    bio "I am very good at things" 
    linked_in "http://my.linkedin.profile.com" 
    website "www.mysite.com" 
    city "London" 

    after(:create) do |profile, evaluator| 
     # update the user email if we specified a value in the invocation 
     profile.user.email = evaluator.user_email unless evaluator.user_email.nil? 
    end 
    end 
end 

allora si dovrebbe essere in grado di richiamare in questo modo e ottenere il risultato desiderato:

p = FactoryGirl.create(:profile, user_email: "[email protected]") 

non l'ho provato, però.

+0

Grazie, ma vorrei che funzionasse per qualsiasi attributo, quindi non vorrei doverlo codificare per ognuno come quello. Forse nessun altro ha bisogno di questo ... – bobomoreno

+2

Penso che il tuo esempio abbia un errore. Cambia 'dopo (: create)' per essere 'profile.user.email = valutator.user_email a meno di valutator.user_email.nil?' – Kelly

18

Secondo uno dei creatori di FactoryGirl, non è possibile passare argomenti dinamici all'assistente dell'associazione (Pass parameter in setting attribute on association in FactoryGirl).

Tuttavia, si dovrebbe essere in grado di fare qualcosa del genere:

FactoryGirl.define do 
    factory :profile do 
    transient do 
     user_args nil 
    end 
    user { build(:user, user_args) } 

    after(:create) do |profile| 
     profile.user.save! 
    end 
    end 
end 

Poi si può chiamare quasi come si voleva:

p = FactoryGirl.create(:profile, user_args: {email: "[email protected]"}) 
+2

Ottima risposta. Vorresti aggiornare questo per rispettare le ultime versioni di Rails. Per esempio. Ricevo un "AVVISO DI AVVISO:' # ignore' è deprecato e verrà rimosso in 5.0. " quando si implementa questa risposta. –

+0

Ho già questo problema in Rails 5 –

+0

Puoi usare "transient" invece di "ignore" per eliminare l'avviso –

3

risolto con la creazione di utenti, e poi il profilo :

my_user = FactoryGirl.create(:user, user_email: "[email protected]") 
my_profile = FactoryGirl.create(:profile, user: my_user.id) 

Quindi, questo è quasi lo stesso che nella questione, diviso in due linee. L'unica differenza reale è l'accesso esplicito a ".id". Testato con Rails 5.