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.