/ / Przyspieszenie asocjacji w specyfikacjach modeli dzięki FactoryGirl - tworzenie vs kompilacja vs build_stubbed - ruby-on-rails, testowanie jednostek, rspec, factory-bot

Przyspieszenie skojarzeń w specyfikacjach modeli z FactoryGirl - create vs build vs build_stubbed - ruby-on-rail, testowanie jednostkowe, rspec, factory-bot

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 № 1

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

Źródło


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