Executable Examples
RSpec provides a Domain Specific Language with which you can express executable
examples of the expected behaviour of a system.
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
When you execute this example, RSpec can provide a report like this:
Account, when first created - should have a balance of $0
So we use RSpec to #describe Behaviour of a system using Examples
of how #it should work.
A Bit More Detail
Here’s how the example might look with some more code filled in:
describe Account, " when first created" do before do @account = Account.new end it "should have a balance of $0" do @account.balance.should eql(Money.new(0, :dollars)) end after do @account = nil end end
The #describe method returns an ExampleGroup object,
which contains a group of examples of a particular behaviour of the system
that you wish to describe. Technically, an ExampleGroup is the
equivalent of a fixture in xUnit-speak. It is a metaphor for the context in
which you will execute your example – a set of known objects in a known
starting state.
The #it method returns an Example object, which represents
an example of the behaviour your are trying to describe.
The #before and #after methods are just
like #setup and #teardown in xUnit. You use them to
set up the state before each example is executed, and tear down any
resources after each example.
#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 before 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
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
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:
describe "all editions", :shared => true 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 its description string, in
this case, “all editions”
All of the following are included from a shared behavior:
- 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:
describe "All Employees", :shared => true do it "should be payable" do @employee.should respond_to(:calculate_pay) end end describe "All Managers", :shared => true 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 new 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
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.
