Jeg er på jakt etter å gjøre Rails-tester raskere. Jeg har bare 520 tester, men det tar 62 sekunder å kjøre i bash, og 82 sekunder å kjøre i Rubymine.
Som et eksempel på en typisk kontrollertest brukte jeg denne koden til å signere inn som @bruker og lage grunnleggende @kommentar i en CommentsController for RSpec-kontrollertestene mine:
before(:each) do
@user = Factory.create(:user)
sign_in @user
@comment = Factory.create(:comment)
end
Som du kanskje skjønner ... er dette tregt. Den bygger en @user
, men bygger også assosiasjonene for den brukeren. Samme for (skjult) jeg trodde å ringe Factory.build (: bruker)
ville løse det ... men jeg får rare feil. current_user
returnerer for eksempel null
.
Så ... Jeg bestemte meg for å bruke Factory.build ()
og stikke ut alle før-filtrene i min foreldrekontroller. Imidlertid sier min rspec-logg fremdeles at TON INNLEGG treffer databasen når jeg inspiserer RSPec-loggen etterpå (vi snakker hundrevis av linjer med kode for bare tre tester!)
before(:each) do
@user = Factory.build(:user)
#sign_in @user
controller.stub(:authenticate_user!) #before_filter
controller.stub(:add_secure_model_data) #before_filter
controller.stub(:current_user).and_return(@user)
@comment = Factory.build(:comment)
end
Det triste faktum er at ovennevnte før (: hver)
-blokk har null effekt på testytelsen. Som jeg oppdaget, vil samtale Factory.build ()
fremdeles internt ringe Factory.create ()
på barneforeninger.
Her er en før (: hver)
blokk som effektivt fjerner søppel produsert i RSpec-loggen. Det ga meg en 35-40% test ytelse boost
before(:each) do
@user = Factory.build(:user, :role => Factory.build(:role))
#sign_in @user
controller.stub(:authenticate_user!)
controller.stub(:add_secure_model_data)
controller.stub(:current_user).and_return(@user)
# both of these are still super slow. WTF?!
@site_update = Factory.build(:site_update, :id => 5, :author => Factory.build(:user, :role => Factory.build(:role)))
@comment = Factory.build(:comment,
:author => Factory.build(:user, :role => Factory.build(:role)),
:commentable => @site_update)
end
Dette gjør at testene går raskere, men det er også stygt som synd. Vi kan ikke skrive dette på alvor for hver test ... gjør vi det? Det er nøtter. Jeg gjør det ikke.
Jeg vil også påpeke at noen av disse Factory.build ()
linjene fortsatt tar ca. 15 sekunder selv om de IKKE treffer databasen!
Å kjøre bare tre tester resulterer fremdeles i omtrent .3 til .35 sekunder tid tatt opp av factory_girl PER-test! Jeg tror det er helt uakseptabelt. Hvis du fjerner linjene Factory.build ()
, går testene på 0,00001 sekunder.
Jeg tror juryen er inne: factory_girl er et veldig tregt bibliotek. Er den eneste løsningen for ikke å bruke den?
Her er mine fabrikker.rb
:
Factory.define :role do |f|
f.name "Admin"
end
Factory.define :user do |f|
f.first_name "Banoo"
f.last_name "Smith"
f.sequence(:email) { |n| "Banoo.Smith#{n}@gmail.com" }
f.password "secretpassword"
f.association :role
end
Factory.define :admin do |f|
f.first_name "Banoo"
f.last_name "Smith"
f.sequence(:email) { |n| "admin#{n}@gmail.com" }
f.password "secretpassword"
f.association :role
end
Factory.define :course_provider do |f|
f.first_name "Josh"
f.last_name "Bolson"
f.sequence(:email) { |n| "josh.bolson#{n}@gmail.com" }
f.password "secretpassword"
f.association :role
end
Factory.define :director do |f|
f.first_name "Director"
f.last_name "Dude"
f.sequence(:email) { |n| "director#{n}@gmail.com" }
f.password "secretpassword"
f.association :role
end
Factory.define :instructor do |f|
f.first_name "Instructor"
f.last_name "Dude"
f.sequence(:email) { |n| "instructor#{n}@gmail.com" }
f.password "secretpassword"
f.association :role
end
Factory.define :trainee do |f|
f.first_name "Trainee"
f.last_name "Dude"
f.sequence(:email) { |n| "trainee#{n}@gmail.com" }
f.password "secretpassword"
f.association :role
end
Factory.define :private_message do |f|
f.subject "Subject"
f.content "content"
f.is_deleted_by_sender false
f.association :sender, :factory => :user
end
Factory.define :recipient do |f|
f.is_read false
f.is_deleted false
f.association :receiver, :factory => :user
f.association :private_message
end
Factory.define :course_template do |f|
f.name "name"
f.description "description"
f.association :course_provider
end
Factory.define :site_update do |f|
f.subject "Subject"
f.intro "intro"
f.content "content"
f.association :author, :factory => :user
end
Factory.define :comment do |f|
f.content "content"
f.association :author, :factory => :user
f.association :commentable, :factory => :site_update
end
Factory.define :country do |f|
f.name "Liberty"
end
Factory.define :province do |f|
f.name "Freedom"
f.association :country
end
Factory.define :payment_plan do |f|
f.name "name"
f.monthly_amount 79
f.audience "Enterprises"
f.active_courses "500-2000"
end
Factory.define :company do |f|
f.name "name"
f.phone_number "455-323-2132"
f.address "address"
f.postal_code "N7G-5F4"
f.association :province
f.association :payment_plan
end
Factory.define :company_user do |f|
f.first_name "Dan"
f.last_name "Grayson"
f.sequence(:email) { |n| "dan.grayson#{n}@gmail.com" }
f.password "secretpassword"
f.association :role
f.association :company
end
Factory.define :course do |f|
f.notes "notes"
f.difficulty 100
f.association :course_template
f.association :instructor, :factory => :company_user
end
Factory.define :study_group do |f|
f.name "name"
end
Factory.define :help_category do |f|
f.name "name"
end
Factory.define :help_document do |f|
f.question "question"
f.content "content"
f.association :category, :factory => :help_category
end
Factory.define :tag do |f|
f.name "name"
end
Factory.define :partial_mapping do |f|
f.from_suffix "ing"
f.to_suffix "ing"
end
Factory.define :newsletter do |f|
f.subject "subject"
f.content "content"
end
Factory.define :press_contact do |f|
f.full_name "Banoo Smith"
f.email 'Banoo.Smith@gmail.com'
f.phone_number "455-323-2132"
f.address "address"
f.postal_code "N9B-3W5"
f.association :province
end
Factory.define :press_release do |f|
f.headline "Headline"
f.origin "origin"
f.intro "intro"
f.body "body"
f.association :contact, :factory => :press_contact
end
Factory.define :theme do |f|
end
Og interessant referanseindeks. Det tar i gjennomsnitt 0,1 til 0,14 sekunder å ringe til Factory.create (: bruker)
:
$ rails runner 'Benchmark.bm {|x| x.report { 100.times { Factory.create(:user) } } }'
user system total real
9.940000 0.080000 10.020000 ( 14.872736)
Selv en Factory.build (: bruker)
tar evig tid. .. og dette er med : default_strategy =>: build
slått på!
$ rails runner 'Benchmark.bm {|x| x.report { 100.times { Factory.build(:user) } } }'
user system total real
9.350000 0.030000 9.380000 ( 11.798339)
Dette er helt klart et bevis på at noe er galt med factory_girl. Løsningen er å kvitte seg med den eller sørge for at den bruker Factory.build
. Det er svaret.
Siden jeg i utgangspunktet har løst mitt eget problem, lurer jeg på hvorfor Factory_girl er så populær, og hvorfor er det "vanlig visdom"? Man kan objektivt konkludere med at uansett hvilke fordeler man kan oppnå ved å bruke Factory Girl - og det er mange fine ting med det - det er ikke verdt ytelseskostnaden. Jeg er sikker på at det kan utvikles en bedre fabrikkperle som er mye mer performant ... men factory_girl er dessverre og beklageligvis ikke det. Jeg tror det er riktig å bruke grunnleggende Ruby, stubber og å fylle ut objektverdiene manuelt per test, hvis du vil unngå inventar og også få høy ytelse når du kjører tester.