There are a lot of things that can slow down your tests - some of them are related to your code and some not. I will focus on quick changes that can make your specs faster and what is more important - better.
Avoid using betruthy and befalsey matchers when you always expect boolean value
Take a look at the code below:
expect(true).to be_truthy
expect(1).to be_truthy
expect('string').to be_truthy
expect(nil).to be_falsey
expect(false).to be_falsey
all of these expectations will pass. So you may expect false
or true
to be returned but when something goes wrong and something else is returned then your test may not catch it.
Solution?
Expect exact value instead:
expect(true).to eq(true)
expect(1).to eq(true)
expect('string').to eq(true)
expect(false)to eq(false)
expect(nil).to eq(false)
Want to receive useful tips, information about new Ruby gems and articles on a daily basis? Make sure you follow me and say hello!
Avoid using FactoryGirl.build
If you used to think that calling FactoryGirl.build will not create records in the database then stop - https://robots.thoughtbot.com/use-factory-girls-build-stubbed-for-a-faster-test. It will when you have associations declared in your factory. For example if you have such factory:
FactoryGirl.define do
factory :user do
contact
company
end
end
and you will call FactoryGirl.build :user
it will create two records in the database. If you initialize your factory at the top of the test and you have 10 examples then you will create 20 records - if you don’t need them then you have a huge area for improvements.
Solution?
Use FactoryGirl.build_stubbed
which will not create any records in the database.
Avoid using Model.new instead of stubbing
Let’s consider following example. We have two simple classes: SampleClass
and SampleApi
class SampleApi
def login; end
end
class SampleClass
def call
api.login
end
private
def api
SampleApi.new
end
end
we want to test SampleClass#call
method:
require 'spec_helper'
describe SampleClass do
describe '#call' do
it 'calls API' do
api = SampleApi.new
allow(SampleApi).to receive(:new).and_return(api)
allow(api).to receive(:login)
sample_class = SampleClass.new
sample_class.call
expect(api).to have_received(:login).once
end
end
end
it looks good. What if we want to add new method to SampleApi
and then call it before #login
method? Let’s do it:
class SampleApi
def login;end
def before_login_action;end
end
class SampleClass
def call
api.before_login_action
api.login
end
private
def api
@api ||= SampleApi.new
end
end
when we run our test, it’s still green. It’s a bad news because we changed the class implementation. When we are using not stubbed instance we are losing control over executed methods. It may be hard to spot such issue, especially, when method is updated by someone who don’t use Test Driven Development approach.
Solution?
Use instance_double
instead. What will happen if we replace api = SampleApi.new
line with api = instance_double(SampleApi, login: double)
? We will receive error:
Double "SampleApi (instance)" received unexpected message :before_login_action with (no args)
Our solution is not slower than previous one and gives us full control. If you combine it with expecting methods to be executed n
times then nothing is able to surprise you.
