Executable Code Examples
RSpec provides a Domain Specific Language with which you can express executable examples of the expected behaviour of your code.
Imagine that you were talking to a customer requesting software for her bank. Part of that conversation might well look like this:
You: Describe an account when it is first created. Customer: It should have a balance of $0.
Here’s how we express that conversation in RSpec:
describe Account, "when first created" do
it "should have a balance of $0" do
…
end
end
The
describe() method returns an ExampleGroup class, which contains a group of examples of a particular behaviour of the system that you wish to describe.
The it() method returns an instance of the ExampleGroup in which that example is run.
When you run this example, RSpec can provide a report like this:
Account when first created - should have a balance of $0
before() and after()
You can use before() and/or after() to define code that executes before and after each example or only once per ExampleGroup:
describe Thing do
before(:all) do
# This is run once and only once, before all of the examples
# and before any before(:each) blocks.
end
before(:each) do
# This is run before each example.
end
before do
# :each is the default, so this is the same as before(:each)
end
it "should do stuff" do
…
end
it "should do more stuff" do
…
end
after(:each) do
# this is after each example
end
after do
# :each is the default, so this is the same as after(:each)
end
after(:all) do
# this is run once and only once after all of the examples
# and after any after(:each) blocks
end
end
Warning: The use of
before(:all) and after(:all) is generally discouraged because it introduces dependencies between the Examples. Still, it might prove useful for very expensive operations if you know what you are doing.
Helper Methods
You can write helper methods directly within an Example Group:
describe "…" do
it "…" do
helper_method
end
def helper_method
…
end
end
Reusable Helper Methods
describe "…" do
it "…" do
helper_method
end
def helper_method
…
end
end
You can include helper methods in multiple ExampleGroups by expressing them within a module, and then including that module in your ExampleGroup:
module AccountExampleHelperMethods
def helper_method
…
end
end
describe "A new account" do
include AccountExampleHelperMethods
before do
@account = Account.new
end
it "should have a balance of $0" do
helper_method
@account.balance.should eql(Money.new(0, :dollars))
end
end
Shared Example Groups
module AccountExampleHelperMethods
def helper_method
…
end
end
describe "A new account" do
include AccountExampleHelperMethods
before do
@account = Account.new
end
it "should have a balance of $0" do
helper_method
@account.balance.should eql(Money.new(0, :dollars))
end
end
You can create shared example groups and include those groups into other groups.
Suppose you have some behavior that applies to all editions of your product, both large and small.
First, factor out the “shared” behavior:
shared_examples_for "all editions" do
it "should behave like all editions" do
end
end
then when you need define the behavior for the Large and Small editions,
reference the shared behavior using the
it_should_behave_like()method.
describe "SmallEdition" do
it_should_behave_like "all editions"
it "should also behave like a small edition" do
end
end
describe "LargeEdition" do
it_should_behave_like "all editions"
it "should also behave like a large edition" do
end
end
it_should_behave_like will search for an ExampleGroup by itsdescription string, in this case, “all editions”
All of the following are included from a shared group:
- before(:all)
- before(:each)
- after(:each)
- after(:all)
- all included modules
- all methods
Shared example groups may not extend classes.
Multiple shared example groups may be referenced in one (non-shared) group.
Shared example groups may be included in other shared groups:
shared_examples_for "All Employees" do
it "should be payable" do
@employee.should respond_to(:calculate_pay)
end
end
shared_examples_for "All Managers" do
it_should_behave_like "All Employees"
it "should be bonusable" do
@employee.should respond_to(:apply_bonus)
end
end
describe Officer do
before(:each) do
@employee = Officer.new
end
it_should_behave_like "All Managers"
it "should be optionable" do
@employee.should respond_to(:grant_options)
end
end
$ spec officer_spec.rb
Officer
- should be payable
- should be bonusable
- should be optionable
Pending Examples
shared_examples_for "All Employees" do
it "should be payable" do
@employee.should respond_to(:calculate_pay)
end
end
shared_examples_for "All Managers" do
it_should_behave_like "All Employees"
it "should be bonusable" do
@employee.should respond_to(:apply_bonus)
end
end
describe Officer do
before(:each) do
@employee = Officer.new
end
it_should_behave_like "All Managers"
it "should be optionable" do
@employee.should respond_to(:grant_options)
end
end
$ spec officer_spec.rb
Officer
- should be payable
- should be bonusable
- should be optionable
There are three ways to mark an example as “pending.”
Leave out the block:
it "should say foo"
The output will say PENDING (Not Yet Implemented).
Use the pending method with no block:
it "should say foo" do
pending("get the vocal chords working")
subject.should say("foo")
end
The output will say PENDING (get the vocal chords working).
Use the pending method with a block:
it "should say foo" do
pending("get the vocal chords working") do
subject.should say("foo")
end
end
The output will say PENDING (get the vocal chords working), and the example will fail telling you to it is FIXED as soon as the body of the block raises no errors.
