How I Fast Test with Rails

Corey Haines has been busy bringing the gospel of fast tests to the Rails world. A few others have since taken up the call and written up their own approach. This is all wonderful but I didn’t quite feel like I could get behind the implementations I’ve seen. So I decided to work out an approach that would fit with my workflow. This article is the result.

What I Want

All the approaches I’ve seen required some change in how your specs & code are organized. This would require subtle changes to my workflow. While not insurmountable, I figured there had to be a cleaner way.

My goals going in were as follows:

  • Navigate my code/specs with existing rails-vim shortcuts
  • Work with code in app/models as well as lib
  • No extra command line requirements
  • Work with my existing guard config

What I Did

Move common RSpec config to spec/config.rb.

# spec/config.rb
RSpec.configure do |config|
  config.mock_with :rspec
  config.treat_symbols_as_metadata_keys_with_true_values = true

Create spec/fast_spec_helper.rb. It adds app/models to the load path & requires common RSpec config. The rspec command already includes the lib directory so we don’t need to worry about that.

# spec/fast_spec_helper.rb
$:.push File.expand_path("../../app/models", __FILE__)
require 'config'

Specs go in spec/models like normal & include the fast_spec_helper as well as the model file.

# spec/models/hello_spec.rb
require 'fast_spec_helper'
require 'hello'

describe Hello do
  its(:world) { should == 'hello world' }

Implementation goes in app/models as you’d expect.

# app/models/hello.rb
class Hello
  def world
    "hello world"

And that’s it! You can now run this fast spec via rspec spec/models/hello_spec.rb same as you ever would. No need to change your Guards and you can use rails-vim’s :A/:Runit to jump around your fast specs & their implementation.

Bonus Points: Split the Fast & Slow

What if you want to run all of your fast, isolated tests before committing to running the slow, framework dependant tests? Easy, just leverage RSpec 2’s meta-data by tagging your examples with fast: true.

# spec/models/hello_spec.rb
require 'fast_spec_helper'
require 'hello'

describe Hello, fast: true do
  its(:world) { should == 'hello world' }
~/$ rspec spec -t fast
~/$ rspec spec -t ~fast