2013-04-30 13 views
22

To jest prawdopodobnie głupie proste, ale nie mogę znaleźć przykładu nigdzie.Zastąpienie FactoryGirl powiązanego obiektu

Mam dwie fabryki:

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 

Co chcę zrobić, to zastąpić niektóre z atrybutów użytkownika podczas tworzenia profilu:

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

lub coś podobnego, ale nie mogę uzyskaj poprawną składnię. Błąd:

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

wiem, że mogę to zrobić przez utworzenie użytkownika, a następnie skojarzenie go z profilu, ale pomyślałem, że musi być lepszy sposób.

Albo to będzie działać:

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

ale to wydaje się zbyt skomplikowane. Czy nie istnieje prostszy sposób na przesłonięcie powiązanego atrybutu? Jaka jest prawidłowa składnia tego?

Odpowiedz

6

Myślę, że możesz zrobić to za pomocą wywołań zwrotnych i atrybutów przejściowych. Jeśli modyfikować swoją fabrykę profil tak:

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 

to powinieneś być w stanie powołać się na to w ten sposób, by uzyskać pożądany rezultat:

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

nie testowałem go, choć.

+0

Dzięki, ale chciałbym, aby działało dla każdego atrybutu, więc nie chciałbym go kodować dla każdego takiego. Może nikt inny tego nie potrzebuje ... – bobomoreno

+2

Myślę, że twój przykład zawiera błąd. Zmień 'after (: create)' na 'profil.user.email = evaluator.user_email, chyba e plik oceniający.user_email.nil?' – Kelly

18

Według jednego z twórców FactoryGirl, nie można przekazywać dynamicznych argumentów do pomocnika stowarzyszenia (Pass parameter in setting attribute on association in FactoryGirl).

Jednakże, powinieneś być w stanie zrobić coś takiego:

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 

Następnie można go nazwać prawie jak chciałeś:

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

Świetna odpowiedź. Czy zaktualizujesz to, aby zachować zgodność z najnowszymi wersjami Railsów? Na przykład. Otrzymuję komunikat "DEPRECATION WARNING:" # ignore "jest przestarzałe i zostanie usunięte w wersji 5.0." podczas wdrażania tej odpowiedzi. –

+0

Mam już ten problem w Railsach 5 –

+0

Możesz użyć "przejściowego" zamiast "ignoruj", aby pozbyć się ostrzeżenia –

3

rozwiązany poprzez stworzenie użytkownik po raz pierwszy, a następnie profil :

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

Jest to prawie to samo, co w pytaniu, podzielone na dwie linie. Jedyną różnicą jest wyraźny dostęp do ".id". Testowane z szynami 5.

Powiązane problemy