Monday, January 29, 2007

RSpec does Autotest

I finally got around to updating to the latest version of RSpec and got it working with Rails and Autotest thanks to a helpful post by Robby On Rails.

But there were a couple of little things that tripped me up:
  1. You must match the versions of the rspec_for_rails plugin with your version of RSpec.
  2. The path structure for 0.7.5.1 has changed from the previous paths. It's now found at svn://rubyforge.org/var/svn/rspec/tags/REL_0_7_5_1/
    rspec_on_rails/vendor/plugins/rspec_on_rails

    (Don't know if this is a bug or not...)
  3. The API has changed to align should_eql/should_equal with ruby's eql?/equal? - a bit different than what I'm used to with Test::Unit, but I suppose more Rubyish. (more info)

Also, running autotest against the spec server really helps with performance, which is key when you're doing test-first development.

Here's how you can automate that in a rake task:
  1. Add your own task at lib/tasks/rspec_autotest.rake (I put it here so it doesn't get clobbered when I update the plugin):

    namespace :spec do

    task :autotest_server do
    require File.join(File.dirname(__FILE__),
    '../../vendor/plugins/rspec_autotest',
    'lib', 'rspec_autotest')
    autotest = RspecOnRailsAutotest.new
    autotest.spec_command = "drbspec --diff unified"
    autotest.run
    end
    end

  2. Kick off the spec server:
    script/rails_spec_server
  3. And start up autotest:
     rake spec:autotest_server

UPDATE:

Oh, and here's how I got autotest color to work with rspec. (Thanks for asking, Scott!)

First, I added this to ~/.autotest to load redgreen which comes with the lastest version of ZenTest.

require 'autotest/redgreen'

Second, I updated my copy of redgreen.rb at /opt/local/lib/ruby/gems/1.8/gems/ZenTest-3.4.3/lib/autotest/redgreen.rb
(your location may vary)

module Autotest::RedGreen
BAR = "=" * 80
RED = 31
GREEN = 32

Autotest.add_hook :ran_command do |at|
if at.results.match /specification/
#rspec
at.results.gsub!(/^.* specifications?, (\d+) failures?$/) { |match|
code = ($1 != "0") ? RED : GREEN
match_string(code, BAR, match)
}
else
at.results.gsub!(/^.* (\d+) failures, (\d+) errors$/) { |match|
code = ($1 != "0" or $2 != "0") ? RED : GREEN
match_string(code, BAR, match)
}
end
end

private

def self.match_string(code, bar, match)
"\e[#{code}m#{bar}\n#{match}\e[0m\n\n"
end
end