On my last project, I eventually settled on using Guard along with Spork to allow for quicker testing. Specifically, I used:

  • rspec
  • spork
  • rb-fsevent
  • guard
  • guard-rspec
  • guard-pow
  • guard-spork

You might be saying, "What the hell are all these gems for?!?!"

Guard

This is a lot of development gem dependencies, but once working, it can vastly grease your testing workflow. Guard is a framework for watching files & doing something when they change. Make sure to use rb-fsevent as well to make detecting files not melt your machine polling for changes.

Guard-rspec

guard-rspec notices that models and/or specs change, and runs the related spec file automatically. Just running one test makes things faster, but you still have to reload all of rails with each run, making the feedback loop completely undesirable.

Spork

This is where Spork comes in. Spork pre-loads your environment, allowing rspec files to run with --drb mode & essentially caching your environment for multiple, fast test runs. That is, unless you change something in the environment (such as config/environment, routes.rb, or Gemile). These changes require spork to be restarted, which is SLOOOOW.

It's not always obvious when spork needs reloading, so sometimes you'd find yourself scratching your head, saying, "this test should be failing... but it's not!" Followed by a head-slap, when you realize you need to bounce spork.

Guard-spork

So guard-spork is a nice way of automatically detecting these changes and reloading spork for you.

Guard-pow

Lastly, I'm using pow from 37signals, so any env related changes that require a server restart automatically call touch tmp/restart.txt and pow restarts the app. Guard-pow does this for you.

Workflow

It's hard to say what all this means for the developer's workflow, but basically all you need to do is run `guard` in a Terminal tab of its own and let it run. I run `guard` almost all day, and it generally does what I need it to.

Life is good, until you try to use this on another project that uses Mongoid & Devise...

The Problem with Mongoid + Spork

Mongoid include its models with Rails, so that means if you change your models you have to reload rails. Not very friendly with a Spork-type model. Hopefully they'll add a lifecycle hook in the future, but until they do, we can utilize a feature of Spork that can save the day: `trap_method/trap_class_method`.

The details are found here, but for the impatient, I modified my `spec_helper`'s prefork block to look like this:

Spork.prefork do<br />  ENV["RAILS_ENV"] ||= 'test'<br /><br />
  require 'rails/mongoid'
  Spork.trap_class_method(Rails::Mongoid, :load_models)

  # Now load our environment
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails' 
  ...

Basically this forces that call to be loaded later on in the lifecycle.

There's a similar interception done for Device, which likes to load models along with Rails routes. You might also need to use this technique to deal with similar issues with Factory Girl, Machinist, and Shoulda Macros as well.

And now my specs are faster again. Yay!

Is the Complexity Worth it?

Is this complexity worth it? Some would say no, and I'm certainly aware of how much additional "gem weight" this adds to a project.

For a great discussion about how to make your specs fast without requiring the complexity of Spork, check out the latest episode of Destroy All Software: Fast Specs with and without Rails. These screencasts are excellent..