I am on a quest to make my Rails tests faster. I only have 520 tests,
but they take 62 seconds to run in bash, and 82 seconds to run in
Rubymine.
As an example of a typical controller test, I was using this code to
sign_in as a @user and create the basic @comment in a
CommentsController for my RSpec controller tests:
before(:each) do
@user = Factory.create(:user)
sign_in @user
@comment = Factory.create(:comment)
end
As you might realize… this is slow. It builds a @user
, but also
builds the associations for that user. Same for the @comment
.
So I thought calling Factory.build(:user)
would solve it… but I
get weird errors. For example, current_user
returns nil
.
So… I decided to use Factory.build()
and stub out all the before
filters in my parent controller. However, my rspec log still says a
TON of inserts are hitting the database when I inspect the RSPec log
afterwards (we are talking hundreds of lines of code for just 3
tests!)
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
The sad fact is, the above before(:each)
block has ZERO effect on
test performance. As I discovered, calling Factory.build()
will
still internally call Factory.create()
on the child associations.
Here is a before(:each)
block that effectively removes the junk
produced in the RSpec log. It gave me a 35-40% test performance 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)
@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
This makes the tests run faster, but it’s also ugly as sin. We can’t
seriously write this for every test… do we? That’s nuts. I’m not
doing it.
I also want to point out that any one of these Factory.build()
lines
still takes about .15 seconds even though they are NOT hitting the
database!
Running only 3 tests still results in about .3 to .35 seconds of time
taken up by factory_girl PER test! I think that is totally
unacceptable. If you remove the Factory.build()
lines, the tests run
in 0.00001 seconds.
I think the jury is in: factory_girl is one really slow library. Is
the only solution to not use it?