Running RSpec as a Rake task

RSpec is a behaviour-driven development tool for Ruby. I like it a lot so far. However, I found the documentation on the site explaining how to run RSpec as a Rake task a bit lacking. I worked it out through trial and error, but thought other people might find my procedure useful.

1. You need Ruby and RubyGems first.

2. Install RSpec. You can get it here. I downloaded it as a gem and installed it from the local file.

3. Install Rake. You can do this using gem (gem install rake) or get it from here.

4. Create your spec file. Here's an example in the file spec_numeric_array.rb:

require 'rubygems'
require_gem 'rspec'

context 'Array of mixed numbers' do
  setup do
    @even_numbers = [2,4,8]
    @odd_numbers = [1,3,7]
    @mixed_numbers = @odd_numbers + @even_numbers + @odd_numbers
  end

  specify 'should return even numbers as an array' do
    @mixed_numbers.even_numbers.should.equal @even_numbers
  end
end

The key parts are the require lines, which make the RSpec library available.

The spec. itself specifies that the behaviour we want to test is whether an array of mixed numbers is able to return only the even numbers it contains. The setup method creates an array of mixed numbers. The specify method states that there should be a method called even_numbers on arrays which returns just even numbers, as a new array. Inside specify, we call this method on the array of mixed numbers, and state that the result should.equal just the even numbers.

The format of spec. files is described in detail on the RSpec homepage.

5. Create a file called Rakefile with this content:

require 'rake'
require 'spec/rake/spectask'

Spec::Rake::SpecTask.new(:spec) do |t|
  t.spec_files = FileList['spec_*.rb']
  t.options = '-v'
end

task :default  => :spec

Again the require statements are the important bit, making the rake and the RSpec rake task libraries available. Another important bit here is the creation of a new spec task, which loads all the files in the directory which match the filename pattern 'spec_*.rb'. We also specify that the spec task is the default task. This is standard Rake stuff.

6. From the command line, in the same directory as the Rakefile, run the task.

rake

You should get something like this:

Array of mixed numbers
- should return even numbers as an array (FAILED - 1)

1)
undefined method `even_numbers' for [1, 3, 7, 2, 4, 8, 1, 3, 7]:Array (NoMethodError)
./spec_numeric_array.rb:14:in `should return even numbers as an array'
/opt/lampp/lib/ruby/gems/1.8/gems/rspec-0.5.3/lib/spec/runner/instance_exec.rb:9:in `instance_exec'
/opt/lampp/lib/ruby/gems/1.8/gems/rspec-0.5.3/lib/spec/runner/specification.rb:16:in `run'
/opt/lampp/lib/ruby/gems/1.8/gems/rspec-0.5.3/lib/spec/runner/context.rb:23:in `run'
/opt/lampp/lib/ruby/gems/1.8/gems/rspec-0.5.3/lib/spec/runner/context.rb:22:in `run'
/opt/lampp/lib/ruby/gems/1.8/gems/rspec-0.5.3/lib/spec/runner/context_runner.rb:38:in `run_specs'
/opt/lampp/lib/ruby/gems/1.8/gems/rspec-0.5.3/lib/spec/runner/context_runner.rb:37:in `run_specs'
/opt/lampp/lib/ruby/gems/1.8/gems/rspec-0.5.3/lib/spec/runner/context_runner.rb:29:in `run'
/opt/lampp/lib/ruby/gems/1.8/gems/rspec-0.5.3/lib/spec/runner/context_runner.rb:10:in `standalone'
/opt/lampp/lib/ruby/gems/1.8/gems/rspec-0.5.3/lib/spec/runner/context.rb:16:in `initialize'
/opt/lampp/lib/ruby/gems/1.8/gems/rspec-0.5.3/lib/spec/runner/kernel_ext.rb:3:in `context'
./spec_numeric_array.rb:6
/opt/lampp/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader.rb:5
/opt/lampp/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake/rake_test_loader.rb:5

Finished in 0.000909 seconds

1 context, 1 specification, 1 failure

7. Create the class which will make the test pass in the file numeric_array.rb:

class Array
  def even_numbers
    delete_if { |n| n if n.is_a?(Integer) and 0 != (n % 2) }
  end
end

This is just a runtime extension to Array, which returns the even numbers in any array.

8. Now require that file in your spec. file:

require 'rubygems'
require_gem 'rspec'
require 'numeric_array'

context 'Array of mixed numbers' do
...
end

In your spec. file, you just need to require any libraries you want to test.

9. Try running rake again, and you should get:

Array of mixed numbers
- should return even numbers as an array

Finished in 0.000673 seconds

1 context, 1 specification, 0 failures

That's it!

Comments

Nice writeup

I have improved some of RSpec's Rake documentation based on this (especially the require stuff).
http://rspec.rubyforge.org/tools/rake.html

Some comments:

1) Do you really need:
require 'rubygems'
require_gem 'rspec'
?

2) A better way to define a default task:
task :default => :spec

3) It's usually a good idea to specify a glob that will also find specs in subdirectories, as exemplified on
http://rspec.rubyforge.org/tools/rake.html
Once your codebase grows, we recommend keeping source tree of specs parallel to lib.

4) I prefer to suffix the files with spec rather than prefixing with spec

Thanks

Thanks for the comprehensive comments, Aslak, most useful. Don't know much about rake, so the shortcut for defining the default task was very helpful.

As far as I can tell, I do really need the require lines for rubygems and for rspec. Without these, my tests won't run.

The glob pointer is useful. As my specs are quite minimal so far, I've not had to move any into subdirectories. I do put the spec files into their own "test" directory, though, and don't mix into lib.

As for suffix vs. prefix, it's just a matter of taste. I find it useful using a prefix, as it keeps all the spec files together in situations where they are mixed with other non-spec files in a single directory.