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?