Sonntag, 26. Juli 2015

Put your ActiveRecord tests on speed!

Suppose your ActiveRecord models already represent the single responsibility pattern. In other words, you've outsourced logic that is not necessarily in a model.
The models are indeed slimmed down, but testing it still takes a long time. Give them some speed.
For example, this simple person_spec.rb based on a standard spec_helper.rb:
require 'spec_helper'

describe Person do
  subject { FactoryGirl.build :person }

  describe "Validation" do
    it { is_expected.to validate_presence_of(:name) }
  end
end
lasts around half a minute.:
ruby$ time RSpec spec/models/person_spec.rb

real 0m32.271s
user 0m15.914s
sys 0m2.938s
and by far the most of the time is needed to start up the Rails application.
In comparison, a processing time for the same tests:
ruby$ time RSpec spec/models/person_spec.rb

real 0m2.977s
user 0m2.548s
sys 0m0.327s
is faster by a factor of about 10.
This can only be achieved if only the necessary modules are loaded by Ruby on Rails. The used spec/active_record_spec_helper.rb looks like:
require 'active_record'
require 'factory_girl'
require 'shoulda/matchers'

connection_info = YAML.load_file("config/database.yml")["test"]
ActiveRecord::Base.establish_connection(connection_info)
RSpec.configure do |config|
  config.around do |Example|
    ActiveRecord::Base.transaction do
      example.run
      raise ActiveRecord::Rollback
    end
  end
end
For the file person_spec.rb then only the requirements have to be changed:
require 'active_record_spec_helper'
require 'person'

describe person do
  subject { FactoryGirl.build :person }

  describe "Validation" do
    it { is_expected.to validate_presence_of(:name) }
  end
end
Each additionally required model must also be explicitly required, which is an advantage. This way the test clearly points out, that there are additional dependencies. They should be dissolved if possible.
Cheers to Corey Haines for promoting this topic.
Further articles of interest:

Supported by Ruby 2.2.1, Ruby on Rails 4.2.1 and RSpec 3.3.2