Not really a problem, but something I didn't really look out for because 
all my tests were passing... and hey, if tests are passing, I sleep well.

*TL;DR Dumped let{}, removed FactoryGirl associations, back to before(:all) 
and instance variable/FG.build pattern = super speedy tests. *
*
*
*TL;DR.1 And read the docs.*

*Background*
I need to test a fairly complex permission matrix at the model level. To 
properly test it, I need to build out several complete company structures, 
with a multitude of users at various authorization levels. And 200+ actual 
tests.

Being a fan of rspec's let{}, I decide to use it copiously. 40 lines worth.

In these tests, I don't really need to have persisted records, so using 
FactoryGirl.build_stubbed fits the bill here.

let(:company) { FactoryGirl.build_stubbed(:company)}

... an so on, 39 times. 

 

*What I Found*
I thought one particular spec was running a little slow. So I converted the 
FactoryGirl.build to the shiny new FactoryGirl.build_stubbed. No difference 
in time. So I decided to watch the test.log. Well, I quickly saw what was 
happening... it was persisting *thousands* of records.

But wait. I was using FactoryGirl.build_stubbed. Where were all these 
records coming from?


*Problem #1*
In most of my FactoryGirl factories, I was using 'association :area', 
'association 
:store', and so on. Didn't think much about these until yesterday.

Turns out that FactoryGirl will build/create and *save* those associated 
factories, even if you are using FactoryGirl.build or 
FactoryGirl.build_stubbed. Learned something new there. Honestly, I didn't 
expect this behavior, but I understand why. Shoulda read the docs.

Now, the easy way around this is to pass into a 
FactoryGirl.create/build/build_stubbed a nil for the association, if it is 
not needed. Ala:

FactoryGirl.build_stubbed(:store, company: fg_company, area: nil)


Now it won't build out the associated area. Alas, I had forgotten *just one*of 
these nil associations. And at the worst possible relationship level, 
the bottom. So every time one factory was built, it create the entire 
supporting structure above. Thus, every hit to an let(:invoice) builds an 
entire company structure from one single 
FactoryGirl.build_stubbed(:invoice)call.

But it get's worse.


*Problem #2*
I love the let{}. But to be honest, I never read the docs on it. Well, I 
did read them yesterday. Relavent line being (emphasis added):

The value will be cached across multiple calls *in the same example but not 
across examples*.


Uh-oh. Let{} is like a before(:each). Which is what most specs need. But I 
don't, not for this spec. I'm never modifying the AR records, just testing 
some methods within models, which don't modify AR instances.


*Resulting Big Problem*
Ok, not really a problem. But certainly very, very inefficient.

By forgetting to nil an association in a FactoryGirld.build_stubbed, and 
with let{} recreating an entire company structure, to the database, for 
every 200+ test. Well, you get the picture. It's slooooow. 22 seconds worth 
of slow.


*Solution*
You know I wouldn't drag you along this far without a solution.

   1. Just remove all the 'association :model' statements from all 
   FactoryGirl definitions. I know they are handy, but I want CONTROL over my 
   factories. And just one small mistake can make a spec run many X-times 
   longer.
   2. Remove the let{} and replace with the good ol' instance 
   variable/build pattern.
   3. Move all the instance variables into a before(:all).

before(:all) do
  @company = FactoryGirl.build_stubbed(:company)

  @store = FactoryGirl.build_stubbed(:store, company: company) 

  ... and so on, 38 times.
end


Note for step #1. It caused me to refactor some other specs as well. This 
turned out to be a good thing, as I was able to speed up several other 
specs, and add some clarity to those specs that required building out a 
company structure.

*Results*
2.5 seconds. Not too shabby.

After the refactoring for all tests, I dropped ~30 more seconds off the 
entire suite.


Hope this helps someone else out there improve the speed of their specs too.

-- 
You received this message because you are subscribed to the Google Groups 
"rspec" group.
To view this discussion on the web visit 
https://groups.google.com/d/msg/rspec/-/tDQBuDgSt_IJ.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/rspec?hl=en.

Reply via email to