Sonntag, 2. August 2015

Speed up ActiveRecord callback tests!

ActiveRecord model tests are often slow when they touch the database.
For example, the simple create callback test in the person_spec.rb:
require 'active_record_spec_helper'
require 'person.rb'

describe Person do
  subject { FactoryGirl.build :person }

  describe "#create" do
    it "should be active" do
      subject.active = nil
      subject.save
      expect(subject.active).to be_truthy
    end
  end
end
It is expected that any new person should be active after creation. The test ensures this.
However, it can be optimized. It is absolutely unimportant to the relevance of the tests that the record is actually stored after passing through the callbacks. The ActiveRecord has already been extensively proven its functionality in the Ruby on Rails test suite.
So there neither has to be opened a connection to the database, nor fired a SQL statement. Therefore, the person_spec.rb also can look like:
require 'active_record_spec_helper'
require 'person.rb'

describe Person do
  subject { FactoryGirl.build :person }

  describe "#create" do
    it "should be active" do
      subject.active = nil
      subject.run_callbacks :create
      expect(subject.active).to be_truthy
    end
  end
end
The test still ensures a new person to be active. And it consumes way less resources. ActiveSupport::Callbacks#run_callbacks calls the callbacks, but without final persistence. A comparison makes the performance difference obvious. With database:
ubuntu$ rspec spec/models/person_spec.rb

Finished in 0.12141 seconds (files took 2 seconds to load)
Without database:
ubuntu$ rspec spec/models/person_spec.rb

Finished in 0.04964 seconds (files took 2 seconds to load)
At first glance it does not look impressive.
But the same test without touching the database is more than twice as fast. The more more callback tests do exist in the project, the more impressive the numbers will be.
Further articles of interest:

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