Speed up Rails tests with Spring

Problem

Did you also run into problem of awfully slow Rails tests?

I built up whole “stack” for my Rails application, RSpec and Capybara were chosen.
At first I attach to many gems (some of them were added unnecessary) which seriously slowed down my tests. So I started to look for a solution for that problem.

Solution

One of the heaviest burden in running tests is loading the Rails environment.
All commands which require loading Rails environment are slowed down with a delay of several seconds.
While running tests the environment is loaded before each test suite.

There are several projects which tries to deal with this problem. The solution is to preload the environment and use POSIX fork. Then for each suite new process is created and all the variables are already loaded as fork copies whole memory used by primary process.

I found following projects: spork, zeus and spring.
In this post I’d like to focus at spring which made the best impression out of all of them.

The reason I prefer spring over other tools is that no changes have to be made in the project to use it.
Additionally, no server has to be run explicitly. In both zeus and spork the server has to be run manually before taking benefits out of that gems.

Spring can speed up following Rails commands:

  1. rspec
  2. rake
  3. cucumber
  4. testunit
  5. rails

As the author of spring explains:

Running spring testunit, spring rake, spring rails, etc gets a bit tedious. It also suffers from a performance issue in Rubygems (which I am actively working on) which means the spring command takes a while to start up. The more gems you have, the longer it takes.

Spring binstubs solve both of these problems. If you will be running the testunit command regularly, run:

$ spring binstub testunit
This generates a bin/spring and a bin/testunit, which allows you to run spring and spring testunit in a way that doesn’t trigger the Rubygems performance bug:

So the spring should give a significant boost to the test suites and the more test you have the bigger difference you notice.

Running tests with Guard

Guard is a tool to easily handle events on file system modifications. There are over 200 hundred plugins for guard.

I created quite simple plugin for guard which invokes spring by executing shell commands.
Default config options added to Guardfile are:

guard 'spring' do  
  watch(%r{^spec/.+_spec\.rb$})
  watch(%r{^spec/spec_helper\.rb$}) { |m| 'spec' }
  watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
  watch(%r{^app/controllers/(.+)_(controller)\.rb$})  do |m|
    %W(spec/routing/#{m[1]}_routing_spec.rb spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb spec/requests/#{m[1]}_spec.rb)
  end
end  

As a result RSpec tests are run after each change in specs or application.
Currently no rules for TestUnit are present but could be added easily.

The guard-spring can be installed by adding the dependency to the application’s Gemfile or by installing it manually with gem.

gem install guard-spring  

Follow the instructions in project’s README to install guard-spring to your application.

Update 2014-05-03

It seems that spring won the race for the best/most popular Rails preloader. It became a part of Rails as of version 4.1.0.