Hvordan kan jeg få Factory Girl til ALDRI å slå databasen hvis jeg ringer Factory.build i orden

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.

21
задан 15 revs 26 May 2011 в 12:13
поделиться