RSpec

Domain Specific Language

Describe behaviour

Ruby code

Readable

Executable

Examples

Design process

Documentation

Tests

describe Game do
  it "should score 0 for gutter game" do
  end
end
describe Game do
  it "should score 0 for gutter game" do
  end
end
$ spec bowling.rb 
./bowling.rb:1: uninitialized constant Game (NameError)
class Game
end
describe Game do
  it "should score 0 for gutter game" do
  end
end
class Game
end
describe Game do
  it "should score 0 for gutter game" do
  end
end
$ spec bowling.rb 
.

Finished in 0.08465 seconds

1 example, 0 failures
class Game
end
describe Game do
  it "should score 0 for gutter game" do
    game.score.should == 0
  end
end
class Game
end
describe Game do
  it "should score 0 for gutter game" do
    game.score.should == 0
  end
end
$ spec bowling.rb 
F

1)
NameError in 'Game should score 0 for gutter game'
undefined local variable or method `game' for #<#<Class:0x14c8d9c>:0x14c89f0>
./bowling.rb:5:

Finished in 0.007298 seconds

1 example, 1 failure
class Game
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    game.score.should == 0
  end
end
class Game
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    game.score.should == 0
  end
end
NoMethodError in 'Game should score 0 for gutter game'
undefined method `score' for #<Game:0x14c84a0>
./bowling.rb:6:
class Game
  def score
  end
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    game.score.should == 0
  end
end
class Game
  def score
  end
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    game.score.should == 0
  end
end
'Game should score 0 for gutter game' FAILED
expected 0, got nil (using ==)
./bowling.rb:8:
class Game
  def score
    0
  end
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    game.score.should == 0
  end
end
class Game
  def score
    0
  end
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    game.score.should == 0
  end
end
$ spec bowling.rb 
.

Finished in 0.006936 seconds

1 example, 0 failures
class Game
  def score
    0
  end
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    1.upto(20) { game.hit(0) }
    game.score.should == 0
  end
end
class Game
  def score
    0
  end
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    1.upto(20) { game.hit(0) }
    game.score.should == 0
  end
end
NoMethodError in 'Game should score 0 for gutter game'
undefined method `hit' for #<Game:0x14c8284>
./bowling.rb:9:
class Game
  def hit
  end
  def score
    0
  end
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    1.upto(20) { game.hit(0) }
    game.score.should == 0
  end
end
class Game
  def hit
  end
  def score
    0
  end
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    1.upto(20) { game.hit(0) }
    game.score.should == 0
  end
end
ArgumentError in 'Game should score 0 for gutter game'
wrong number of arguments (1 for 0)
./bowling.rb:11:in `hit'
class Game
  def hit(pins)
  end
  def score
    0
  end
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    1.upto(20) { game.hit(0) }
    game.score.should == 0
  end
end
class Game
  def hit(pins)
  end
  def score
    0
  end
end
describe Game do
  it "should score 0 for gutter game" do
    game = Game.new
    1.upto(20) { game.hit(0) }
    game.score.should == 0
  end
end
$ spec bowling.rb 
.
$ spec bowling.rb --format specdoc

Game
- should score 0 for gutter game
- should score 300 for perfect game
- should score 90 for all 9s

Finished in 0.006989 seconds

3 examples, 0 failures

Spec::Rails

# Model spec
require File.dirname(__FILE__) + '/../spec_helper'

describe Person, "with fixtures loaded" do
  fixtures :people, :animals

  before(:each) do
    # fixtures are setup before this
  end

  it "should have a non-empty collection of people" do
    Person.find(:all).should_not be_empty
  end

  it "should have one record" do
    Person.should have(1).record
  end
end
# Controller spec
require File.dirname(__FILE__) + '/../spec_helper'

describe PeopleController do
  before(:each) do
    @person = mock_model(Person)
    Person.stub!(:new).and_return(@person)
  end

  it "should create a new, unsaved person on GET to create" do
    Person.should_receive(:new).and_return(@person)
    get 'create'
  end

  it "should assign new person to template on GET to create" do
    get 'create'
    assigns[:person].should equal(@person)
  end
end
# Controller spec
describe PeopleController do
  before(:each) do
    @person = mock_model(Person)
    Person.stub!(:new).and_return(@person)
  end

  it "should redirect to index after successful create" do
    @person.should_receive(:save).and_return(true)
    post 'create'
    response.should redirect_to('index')
  end

  it "should re-render new after failed create" do
    @person.should_receive(:save).and_return(false)
    post 'create'
    response.should render_template('create')
  end
end
# View spec
describe "/people/list" do
  it "should display the list of people" do
    smith = mock_model(Person)
    smith.should_receive(:name).and_return("Smith")
    assigns[:people] = [smith]

    render "/people/list"

    response.should have_tag('ul') do
      with_tag('li', 'Name: Smith')
    end
  end
end

Spec::Rails Installation

Install plugins

$ ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_1_0_0/rspec
$ ruby script/plugin install svn://rubyforge.org/var/svn/rspec/tags/REL_1_0_0/rspec_on_rails

Bootstrap

$ ruby script/generate rspec

Rake

$ rake spec
$ rake -T | grep spec

Generators

ruby script/generate rspec_resource purchase order_id:integer amount:decimal