Powiedz, że mam modele User
i Post
, użytkownik has_many
posty i post belongs_to
użytkownik.
Kiedy piszę specyfikację dla Post
, moim pierwszym odruchem jest napisanie czegoś takiego:
before do
@user = FactoryGirl.create :user
@post = @user.posts.new(title: "Foo", content: "bar)
end
... tests for @post go here ...
Ale to stworzy nowego użytkownika - trafieniebaza danych - dla każdego testu, co spowolni działanie. Czy jest lepszy sposób na zrobienie tego, który przyspieszy moje testy i pozwoli uniknąć tak częstego uderzania w DB?
Jak rozumiem, nie mogę używać FactoryGirl.build :user
ponieważ, mimo że nie trafi do DB, skojarzenia nie będą działać poprawnie, ponieważ @user
nie będzie mieć identyfikatora i tak @post.user
nie działa (zwraca nil
.)
mógłbym użyć FactoryGirl.build_stubbed :user
co stworzyło „fałszywy trwały” @user
który ma identyfikator, ale @post.user
nadal zwraca zero. Robi build_stubbed
mają jakąkolwiek praktyczną przewagę nad build
kiedy testuję rzeczy związane ze skojarzeniami?
Chyba mógłbym użyć build_stubbed
kikut @post.user
więc wraca @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ć uderzenie prędkości?
Jedyną inną alternatywą, jaką przychodzi mi do głowy, byłoby skonfigurowanie @user w pliku before(:all)
blok, który 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ąć tworzenia zbyt wielu zapytań do bazy danych?
Odpowiedzi:
18 dla odpowiedzi № 1Jeś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 używania before(:all)
. Nie jest wykonywany w transakcji. Więc cokolwiek tworzysz w before(:all)
zostanie w bazie danych i może spowodować konflikt z innymi testami
O FactoryGirl.build
, buduje obiekt, ale tworzy skojarzenia.
Na przykład:
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 dla odpowiedzi nr 2
Krótka odpowiedź
@user = FactoryGirl.build_stubbed(:user)
@post = FactoryGirl.build_stubbed(:post, :user => @user)
To sprawi, że @ post.user będzie działał bez wchodzenia do bazy danych.
Długa odpowiedź
Radziłbym poczekać na before
blokuj, dopóki nie upewnisz się, że jest to potrzebne. Zamiast tego twórz dane potrzebne do każdego testu i wyodrębnij duplikaty do metod lub nowych fabryk, gdy tylko je znajdziesz.
Czy w każdym teście musisz odwoływać się do użytkownika? Mający @user
dostępne w każdym teście mówi innym programistom, że jest ważne wszędzie.
Na koniec, zakładając, że skojarzenie użytkownika jest również zadeklarowane w twojej fabryce pocztowej, automatycznie uzyskasz działanie post.user
kiedy to zrobisz build_stubbed(:post)
.
8 dla odpowiedzi nr 3
Łatwo jest zapomnieć o różnicach między nimi create
, build
, i build_stubbed
. Oto krótka informacja dla osób w tej samej sytuacji (ponieważ ta strona zajmuje wysokie miejsce 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
1 dla odpowiedzi nr 4
Z dokumentu dziewczyny z fabryki możesz zidentyfikować strategię build
dla user
w związku w post
fabryka taka jak ta:
factory :post do
association :user, factory: :user, strategy: :build
end
Dzięki czemu można build
za post
bez zapisywania user
post = build(:post)
post.new_record? # => true
post.author.new_record? # => true
0 dla odpowiedzi № 5
Szybkie wyjaśnienie różnic: FactoryGirl.create utworzy dla niego nowy obiekt i skojarzenia (jeśli fabryka je posiada). Wszystkie zostaną utrwalone w db. Ponadto wyzwoli zarówno walidację modelu, jak i bazy danych. Wywołania zwrotne po (: build) i po (: create) zostaną wywołane po zapisaniu fabryki. Również przed (: create) zostanie wywołane przed zapisaniem fabryki.
FactoryGirl.build nie zapisuje obiektu, ale nadal wysyła żądania do bazy danych, jeśli fabryka ma skojarzenia. Spowoduje to uruchomienie walidacji tylko dla powiązanych obiektów. Callback po (: build) zostanie wywołany po zbudowaniu fabryki.
FactoryGirl.build_stubbed w ogóle nie wywołuje bazy danych. Tworzy i przypisuje atrybuty do obiektu, aby zachowywał się jak obiekt, którego instancja została utworzona. Zapewnia fałszywy identyfikator i created_at. Skojarzenia, jeśli istnieją, również zostaną utworzone za pośrednictwem build_stubbed. Nie spowoduje to żadnej weryfikacji.
Przeczytaj pełne wyjaśnienie tutaj