2013-12-18 13 views
8

Załóżmy, że mam modele User i Post, użytkownika has_many i posta belongs_to użytkownika.Przyspieszenie skojarzeń ze specyfikacją modelu za pomocą FactoryGirl - utwórz vs buduj, a następnie zainstaluj ponownie.

Kiedy piszę spec Post, moim pierwszym odruchem jest napisać coś takiego:

before do 
    @user = FactoryGirl.create :user 
    @post = @user.posts.new(title: "Foo", content: "bar) 
end 

... tests for @post go here ... 

Ale to się dzieje, aby utworzyć nowego użytkownika - uderzanie bazy danych - dla każdego testu, który jest zamierzam spowolnić działania. Czy istnieje lepszy sposób na zrobienie tego, co przyspieszy moje testy i uniknie tak częstego uderzania w DB?

Jak rozumiem, nie mogę używać FactoryGirl.build :user ponieważ, mimo że nie trafi na DB, stowarzyszenia nie będzie działać poprawnie ponieważ @user nie będzie miał identyfikator i tak @post.user nie zadziała (zwraca nil).

mogę użyć FactoryGirl.build_stubbed :user który stworzył „Fake utrzymywały” @user który ma identyfikator, ale @post.user nadal zwraca nil. Czy build_stubbed ma jakąś praktyczną przewagę nad build, kiedy testuję rzeczy związane z skojarzeniami?

Przypuszczam, że mógłbym użyć kodu build_stubbed stub @post.user, więc zwraca @user ... czy jest jakiś powód, dla którego może to być zły pomysł?

Czy powinienem po prostu użyć create i zaakceptować prędkość uderzenia?

Jedyną alternatywą, o której mogę pomyśleć, to ustawienie @user w bloku before(:all), co wydaje się złym pomysłem.

Jaki jest najlepszy sposób pisania tego rodzaju testów w czysty, zwięzły sposób, który pozwala uniknąć zbyt wielu zapytań DB?

Odpowiedz

18

Jeśli nie chcesz, aby twoje testy trafiały do ​​bazy danych, musisz to zrobić.

before do 
    @user = FactoryGirl.build_stubbed :user 
    @post = FactoryGirl.build_stubbed :post 
    @user.stub(:posts).and_return([@post]) 
    @post.stub(:user).and_return(@user) 
end 

Uwaga: Zachowaj ostrożność podczas korzystania z before(:all). Nie zostanie wykonany w transakcji. Tak więc wszystko, co utworzysz w before(:all), zostanie pozostawione w bazie danych i może spowodować konflikt z innymi testami.

O obiekcie FactoryGirl.build buduje obiekt, ale tworzy powiązania.

Dla np:

factory :user do 
    association posts 
end 

FactoryGirl.build(:user) #this creates posts in the database even though you are only building the parent object(user) 
15

Krótka odpowiedź

@user = FactoryGirl.build_stubbed(:user) 
@post = FactoryGirl.build_stubbed(:post, :user => @user) 

To uczyni pracę @ post.user nigdy nie trafiając do bazy danych.

długa odpowiedź

Moja rekomendacja byłoby poczekać na bloku before dopóki masz pewność, że jest to potrzebne. Zamiast tego buduj dane potrzebne do każdego pojedynczego testu i usuwaj duplikaty metod lub nowych fabryk, gdy je znajdziesz.

Czy w każdym pojedynczym teście należy podać odniesienie do użytkownika? Posiadanie @user dostępnej w każdym teście mówi innym programistom, że jest to ważne wszędzie.

Na koniec, zakładając, że stowarzyszenie użytkowników zostanie również zadeklarowane w zakładzie pocztowym, automatycznie otrzymasz numer post.user po wykonaniu build_stubbed(:post).

8

Łatwo jest zapomnieć o różnicach między create, build, a build_stubbed. Oto krótkie odniesienie dla osób znajdujących się w tej samej sytuacji (ponieważ strona ta znajduje się wysoko w wynikach wyszukiwania).

# Returns a User instance that's not saved (does not write to DB) 
user = build(:user) 

# Returns a saved User instance (writes to DB) 
user = create(:user) 

# Returns a hash of attributes that can be used to build a User instance 
attrs = attributes_for(:user) 

# Returns an object with all defined attributes stubbed out 
stub = build_stubbed(:user) 

# Passing a block to any of the methods above will yield the return object 
create(:user) do |user| 
    user.posts.create(attributes_for(:post)) 
end 

Source

1

Z dokumentu Dziewczyna z fabryki, można określić strategię build dla user w związku z post fabryce tak:

factory :post do 
    association :user, factory: :user, strategy: :build 
end 

aby można buildpost bez zapisywania user

post = build(:post) 
post.new_record?  # => true 
post.author.new_record? # => true 
Powiązane problemy