Nice write up, thanks for sharing.
On Monday, July 9, 2012 10:26:18 AM UTC-7, Karl wrote:
>
> 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/-/MIkevtgDxVAJ.
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.