RSpec, Cucumber, Webrat, RCov and Autotest are a powerful combination of tools for testing your Rails app. Unfortunately getting them to all work nicely together can be a bit of challenge. I recently configured a development environment from scratch on OS X 10.5 Leopard and kept track of all of the little details.
Prerequisites
I’m assuming you’ve got the following installed:
- ruby
- ruby gems 1.3.1
- Apple development tools
- git
- rails >= 2.3.2
- You’ve added github to your gem sources (gem sources -a http://gems.github.com)
RSpec & RSpec-Rails
First let’s grab the rspec1 and rspec-rails2 gems.
sudo gem install rspec
sudo gem install rspec-rails
Cucumber
Next we’ll install the cucumber3 gem
sudo gem install cucumber
Webrat
Webrat4 is used by cucumber to simulate a browser for your integration tests. Webrat will also install nokogiri5.
sudo gem install webrat
RCov
I thought RCov6 would get installed with RSpec, but it wasn’t for me. You might not need to do this, but just to make sure…
sudo gem install rcov
Autotest
Autotest7 comes from ZenTest8 and allows you to have a kick ass workflow where you are constantly running relevant tests and less-constantly automatically running your entire test suite.
sudo gem install ZenTest
Optionally, Thoughtbot’s Factory Girl
Factory girl9 is a really helpful fixture replacement (and more) gem to use in conjunction with cucumber, checkout their much better explanation
sudo gem install thoughtbot-factory_girl --source http://gems.github.com
Optionally, Carlos Brando’s Autotest Notification
While autotest normally runs in a terminal window, it can be setup to hook into applications like growl or snarl. The Autotest Notification9 gem helps make this setup a lot easier.
You will need growl installed and configured for this step the installation instructions on this gems github page are very easy to follow.
sudo gem install carlosbrando-autotest-notification --source=http://gems.github.com
Next you need to turn autotest notifications “on”
an-install
A Sample Rails App
Let’s create a sample rails app for the rest of this guide.
rails sample-app
Configuring Environment Variables
Autotest relies on some environment variables to run all of your features and specs correctly. If autotest “hangs” after you try to run it, or it just never seems to be watching your specs or features, this will most likely solve your problem.
Open the test.rb environment definition file in sample-app/config/environments/test.rb and add the following.
1 2 | ENV['AUTOFEATURE'] = "true" ENV['RSPEC'] = "true" |
These lines will test autotest to run, and look for changes to, your specs (rather than test unit tests) and your cucumber features.
Update
If you don’t want to add these environment variables to every rails project you’ve got on your machine, you can also choose to set them as environment variables in your .bash_profile or .bashrc (or whatever shell you’re using) files.
export AUTOFEATURE=true export RSPEC=true
Unpacking Gems
Next let’s freeze (unpack) some gems that we’ll be using in our app. I’ve run into problems trying to use the system gems with cucumber, rspec and webrat, especially when I have multiple versions of any of them installed. Unpacking them into my rails app solves this problem for me.
mkdir sample-app/vendor/gems cd sample-app/vendor/gems gem unpack rails gem unpack rspec gem unpack rspec-rails gem unpack cucumber
Because webrat (and nokogiri) are native gems, that is, they are built locally on your machine based on its architecture, we won’t unpack those.
config.gem support
The current accepted practice, when using rails 2.3, and as suggested by the rspec guy(s) is to use rails’ config.gem functionality.
Open sample-app/config/environments/test.rb and add the following lines:
config.gem "rspec", :lib => false, :version => ">= 1.2.0" config.gem "rspec-rails", :lib => false, :version => ">= 1.2.0" config.gem "cucumber", :lib => false, :version => ">= 0.2.3" config.gem "thoughtbot-factory_girl", :lib => "factory_girl", :source => "http://gems.github.com" config.gem "webrat", :lib => false, :version => ">= 0.4.3" config.gem "nokogiri", :lib => false, :version => ">= 1.2.3"
Your version numbers may be different, but these are all current at the time of writing.
Boot Strapping RSpec and Cucumber
Before you can get very far with rspec or cucumber you need to run the bootstrapping scripts to give yourself the default files and directories.
# From inside your rails app sample-app/ script/generate rspec script/generate cucumber
Factories
Depending on where you’re going to use your factories the most, you might want to save your file in either spec/ or features/. I chose the latter. Only complete this step if you plan to use the FactoryGirl gem.
touch sample-app/features/factories.rb
Getting Accurate RCov Data
By default RCov is setup to only use your specs when calculating code coverage. If you’re using Cucumber and RSpec, you’ll obviously want to include both types of tests to calculate your project’s true code coverage.
I picked up this rcov rake task from my co-worker Jay McGavren it does all of the heavy lifting for you, we’ll just need to make a couple of changes.
Drop this file into sample-app/lib/tasks/rcov.rake and use it by calling rake rcov:all from your terminal.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | require 'cucumber/rake/task' #I have to add this require 'spec/rake/spectask' namespace :rcov do Cucumber::Rake::Task.new(:cucumber) do |t| t.rcov = true t.rcov_opts = %w{--rails --exclude osx/objc,gems/,spec/,features/ --aggregate coverage.data} t.rcov_opts << %[-o "coverage"] end Spec::Rake::SpecTask.new(:rspec) do |t| t.spec_opts = ['--options', ""#{RAILS_ROOT}/spec/spec.opts""] t.spec_files = FileList['spec/**/*_spec.rb'] t.rcov = true t.rcov_opts = lambda do IO.readlines("#{RAILS_ROOT}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten end end desc "Run both specs and features to generate aggregated coverage" task :all do |t| rm "coverage.data" if File.exist?("coverage.data") Rake::Task["rcov:cucumber"].invoke Rake::Task["rcov:rspec"].invoke end end |
The important part here is on line 7, we want rcov to exclude our features directory. We obviously don’t need or want rcov telling us that our feature files are not “covered”. To solve this problem we’ve simply excluded the features directory from rcov’s processing.
We also need to slightly modify sample-app/spec/rcov.opts to get the full rspec + cucumber coverage data.
Your rcov.opts should look like this:
--exclude "spec/*,gems/*,features/*" --rails --aggregate "coverage.data"
We again want to ignore our cucumber features and we also want to tell rcov to aggregate data in a file called coverage.data. This is used in the above rake task.
Write Some Specs and Features!
Act like you know what you’re doing and write some models, controllers whatever. Add some specs and features too.
Autotest Workflow
Open a terminal and make your way to your sample rails app and fire up autotest. You might see something like the following, depending on how many specs and features you’ve got.
$> autotest loading autotest/cucumber_rails_rspec opts ... Finished in 0.06276 seconds 3 examples, 0 failures ================================================================================ /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby /Library/Ruby/Gems/1.8/gems/cucumber-0.2.3/bin/cucumber --format progress --format rerun --out /var/folders/Aq/Aqp06i3dFnqse+tQgQA+1++++TI/-Tmp-/autotest-cucumber.75956.0 features ................. 4 scenarios 17 passed steps /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby /Library/Ruby/Gems/1.8/gems/rspec-1.2.2/bin/spec --autospec spec/models/intern_spec.rb -O spec/spec.opts ... Finished in 0.062995 seconds 3 examples, 0 failures ================================================================================ /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby /Library/Ruby/Gems/1.8/gems/cucumber-0.2.3/bin/cucumber --format progress --format rerun --out /var/folders/Aq/Aqp06i3dFnqse+tQgQA+1++++TI/-Tmp-/autotest-cucumber.75956.1 features ................. 4 scenarios 17 passed steps
The REALLY important stuff
- make sure you’ve got “ENV['AUTOFEATURE'] = true” in your test.rb otherwise autotest won’t run your features automatically
- make sure you’ve got “ENV['RSPEC'] = true” in your bash profile or else autotest won’t run your specs automatically
- make sure you’ve got “–aggregate = ‘coverage.data’” in your spec/rcov.opts file if you’re going to use the above rake task and hope to get combined rcov coverage data between rspec and cucumber
- make sure you’re excluding the features directory from rcov where required or else you’ll end up with misleading rcov data.
Gem Versions
Here’s a list of the current gems and their versions that I used in preparing this guide.
*** LOCAL GEMS *** actionmailer (2.3.2, 1.3.6, 1.3.3) actionpack (2.3.2, 1.13.6, 1.13.3) actionwebservice (1.2.6, 1.2.3) activerecord (2.3.2, 1.15.6, 1.15.3) activeresource (2.3.2) activesupport (2.3.2, 1.4.4, 1.4.2) acts_as_ferret (0.4.1) addressable (2.0.2) builder (2.1.2) capistrano (2.0.0) carlosbrando-autotest-notification (1.9.1) cgi_multipart_eof_fix (2.5.0, 2.2) cucumber (0.2.3) daemons (1.0.9, 1.0.7) data_objects (0.9.11) diff-lcs (1.1.2) dnssd (0.6.0) extlib (0.9.11) fastthread (1.0.1, 1.0) fcgi (0.8.7) ferret (0.11.4) gem_plugin (0.2.3, 0.2.2) highline (1.2.9) hpricot (0.6) libxml-ruby (0.3.8.4) mongrel (1.1.4, 1.0.1) mysql (2.7) needle (1.3.0) net-sftp (1.1.0) net-ssh (1.1.2) nokogiri (1.2.3) polyglot (0.2.5) rack (0.9.1) rails (2.3.2, 1.2.6, 1.2.3) rake (0.8.4, 0.7.3) rcov (0.8.1.2.0) RedCloth (3.0.4) rspec (1.2.2) rspec-rails (1.2.2) ruby-openid (1.1.4) ruby-yadis (0.3.4) rubynode (0.1.3) sources (0.0.1) sqlite3-ruby (1.2.1) term-ansicolor (1.0.3) termios (0.9.4) textmate (0.9.2) thor (0.9.9) thoughtbot-factory_girl (1.2.0) treetop (1.2.5) webrat (0.4.3) ZenTest (4.0.0)
El Fin
Hopefully this guide was useful or had that one little step that you needed to get everything working. I’m sure this will all be out of date in the coming weeks, but I’ll try to keep it as up-to-date as possible. If you see any errors, or can better explain some of the missing pieces, please post a comment. Thanks!
1 http://github.com/dchelimsky/rspec/tree/master
2 http://github.com/dchelimsky/rspec-rails/tree/master
3 http://github.com/aslakhellesoy/cucumber/tree/master
4 http://wiki.github.com/brynary/webrat
5 http://github.com/tenderlove/nokogiri/tree/master
6 http://rubyforge.org/projects/rcov/
7 http://www.zenspider.com/ZSS/Products/ZenTest/#rsn
8 http://www.zenspider.com/ZSS/Products/ZenTest/
9 http://github.com/thoughtbot/factory_girl/tree/master
10 http://github.com/carlosbrando/autotest-notification/tree/master
Updates
2009-12-08 – Removed “sudo” when describing how to unpack gems (h/t xdotcommer)