Friday, May 18, 2007

rSpec - behavior driven development framework for Ruby

My friend Wayne (who is at RailsConf right now, lucky) introduced me to rSpec, a behavior driven development framework for Ruby.

In short, according to Dave Astels (rSpec core developer), if you are doing test driven development (TDD) properly you are already doing behavior driven development (BDD).

BDD takes the concept of test first and puts it in a behavior specific domain. Why would someone use a BDD framework over a TDD framework? BDD frameworks like rSpec don't do anything that other unit testing frameworks can't do, they just put it in terms of behavior specification instead of generic assertions. To help the point get across I present this analogy:
There is no reason a unit test framework cannot be used to specify behavior of a piece of code, but then again there is a reason why C might not be the best choice of language to implement an object oriented design.


An example is worth a thousand descriptions: say we want a really cool car, one that can top out around 200 mph and run a quarter mile in 10 seconds (give or take a second.)

Using Test::Unit we can specify the behavior thusly:
require 'test/unit'

require 'cool_car'

class CoolCarTest < Test::Unit::TestCase

def setup
@car = CoolCar.new
end

def test_quickness
@car.accellerate until @car.distance >= 0.25
assert_in_delta 10, @car.time_driven, 1.1
end

def test_speed
last_speed = -1

until @car.speed == last_speed
last_speed = @car.speed
@car.accellerate
end

assert_in_delta 200, @car.speed, 1.1
end

end

not bad, we specified the behavior of a car that will do what we want, nothing more, nothing less. lets try it again but in rSpec:
require 'cool_car'

describe CoolCar do

before(:each) do
@car = CoolCar.new
end

it "should run a quarter mile in 10 +/- 1.1 seconds" do
@car.accellerate until @car.distance >= 0.25
@car.time_driven.should be_close(10, 1.1)
end

it "should have a top speed of 180 mph or better" do
last_speed = -1

until @car.speed == last_speed
last_speed = @car.speed
@car.accellerate
end

@car.speed.should be_close(200, 1.1)
end

end

Ahh much nicer.

here is cool_car.rb:
class CoolCar

attr_reader :distance, :speed, :time_driven

def initialize
@distance = 0.0 # in miles
@speed = 0.0 # in mph
@time_driven = 0.0 # in seconds
end

# accellerate for one second
def accellerate
@time_driven += 1
@speed += 1 if @speed < 200
@distance += 0.025
end

end


The laws of physics don't really apply, but the spec does! (send me a class for a CoolCar that is a little more realistic and i'll update this, you have a spec).

BDD is more than a cute way to write tests in a more natural language, it's a way to specify semantically and syntactically behavior (and only behavior) of of software. From what is demonstrated above we can clearly see that BDD and rSpec are must see stops along the path of agile enlightenment.

For a great intro to BDD and rSpec check out Dave Astels' talk: