Introducing rbehave

rbehave is a framework for defining and executing application requirements. Using the vocabulary of behaviour-driven development, you define a feature in terms of a Story with Scenarios that describe how the feature behaves. Using a minimum of syntax (a few “quotes” mostly), this becomes an executable and self-describing requirements document.

BDD has been around in the Ruby world for a while now, in the form of the excellent rspec framework, which describes the behaviour of objects at the code level. The rspec team has focused on creating a simple, elegant syntax and playing nicely with other frameworks, in particular Rails and the Mocha mocking library.

Inspired by this, I wanted to find a simple and elegant way in Ruby to describe behaviour at the application level. This is a different enough problem that I couldn’t just use rspec. You can skip ahead to the Ruby code if you already know about stories and scenarios. This preamble just sets the scene for the example.

The scenarios that describe a story are made up of “steps” of Givens, Events and Outcomes. These steps can be mixed and matched in different ways to provide different sequences of events. Here is an example:

Story: transfer to cash account
  As a savings account holder
  I want to transfer money from my savings account
  So that I can get cash easily from an ATM

  Scenario: savings account is in credit
    Given my savings account balance is \$100
    And my cash account balance is \$10
    When I transfer \$20
    Then my savings account balance should be \$80
    And my cash account balance should be \$30

  Scenario: savings account is overdrawn
    Given my savings account balance is -\$20
    And my cash account balance is \$10
    When I transfer \$20
    Then my savings account balance should be -\$20
    And my cash account balance should be \$10

Here we have two givens: one about my savings account and the other about my cash account. We have a single event, namely transferring cash. We have two outcomes, again about the account balances.

This is typical of the scenarios in a story: they revolve around a single event (the feature itself) and prescribe different outcomes for different combinations of givens. Also, notice that the steps themselves are parameterized: the first time my savings account balance is $100, the second time it is -$20, so a story framework needs to accommodate this.

Getting it running

So then I converted the story into a rspec-like structure, preferring simple strings to method names, and do/end blocks rather than classes:

require 'rubygems'
require 'rbehave'

Story "transfer to cash account",
%(As a savings account holder
  I want to transfer money from my savings account
  So that I can get cash easily from an ATM) do

  Scenario "savings account is in credit" do
    Given "my savings account balance is", 100
    Given "my cash account balance is", 10
    When "I transfer", 20
    Then "my savings account balance should be", 80
    Then "my cash account balance should be", 30
  end

  Scenario "savings account is overdrawn" do
    Given "my savings account balance is", -20
    Given "my cash account balance is", 10
    When "I transfer", 20
    Then "my savings account balance should be", -20
    Then "my cash account balance should be", 10
  end
end

Using rspec to drive the design, I wrote a little framework that would run these scenarios, each one in its own instance of an object (so they were independent), reusing the steps as needed.

So, this only left the problem of the steps themselves. They would have to be defined somewhere else (that I hadn’t figured out yet). Then I thought: each step’s implementation should be pretty trivial, so what would happen if I put the code for each step inline in the scenario? So I ended up with this:

require 'rubygems'
require 'rbehave'
require 'spec' # for "should" method

require 'account' # the actual application code

Story "transfer to cash account",
%(As a savings account holder
  I want to transfer money from my savings account
  So that I can get cash easily from an ATM) do

  Scenario "savings account is in credit" do
    Given "my savings account balance is", 100 do |balance|
      @savings_account = Account.new(balance)
    end
    Given "my cash account balance is", 10 do |balance|
      @cash_account = Account.new(balance)
    end
    When "I transfer", 20 do |amount|
      @savings_account.transfer_to(@cash_account, amount)
    end
    Then "my savings account balance should be", 80 do |expected_amount|
      @savings_account.balance.should == expected_amount
    end
    Then "my cash account balance should be", 30 do |expected_amount|
      @cash_account.balance.should == expected_amount
    end
  end

  Scenario "savings account is overdrawn" do
    Given "my savings account balance is", -20
    Given "my cash account balance is", 10
    When "I transfer", 20
    Then "my savings account balance should be", -20
    Then "my cash account balance should be", 10
  end
end

For this example it turns out there are no new steps to define in the second scenario, which makes it very easy to read. In general I’m finding that most of the steps get defined in the first one or two scenarios.

Implementing the code

So I saved this to a file as transfer_funds.rb and ran it, and I got two failures:

Running 2 scenarios:
FF

2 scenarios: 0 succeeded, 2 failed

FAILURES:
1) transfer to cash account (savings account is in credit) FAILED
NameError: uninitialized constant Account
...
2) transfer to cash account (savings account is overdrawn) FAILED
NameError: uninitialized constant Account
...

rbehave prints one character per scenario – a dot means the scenario passed, an F means it failed. At the end of the run it prints a list of the failing scenarios. So this tells me that firstly it runs (hooray!) and secondly both scenarios are failing because I’m missing an Account class. Well I don’t want a whole bunch of failing scenarios that only start to work one at a time as I implement them. That feels too much like broken windows – I’ll get too used to seeing failing scenarios and then I won’t react when workng scenarios start failing. So I introduced the idea of a “pending” scenario, by adding a pending() method:

Given "my savings account balance is", 100 do |balance|
  pending "needs an Account"
  <code>savings_account = Account.new(bal)
end

And I got this:

Running 2 scenarios:
PP

2 scenarios: 0 succeeded, 0 failed, 2 pending

Pending:
1) transfer to cash account (savings account is in credit): needs an Account
2) transfer to cash account (savings account is overdrawn): needs an Account

The Ps represent pending scenarios, which means they aren’t working yet but they don’t count as a failure. Then I use rspec to implement an Account with a constructor that takes an initial balance, and give it a transfer_to@ method that moves money around. Then I remove the pending line:

Running 2 scenarios:
.F

2 scenarios: 1 succeeded, 1 failed, 0 pending

FAILURES:
1) transfer to cash account (savings account is overdrawn) FAILED
Spec::Expectations::ExpectationNotMetError: expected -20, got -40 (using ==)
...

Excellent! I have my first working scenario. Now I just need to add behaviour to the Account class to not transfer money it doesn’t have! But wait a minute, what about documentation? Well I added some listeners to the story runner, so when you run:

transfer_funds.rb --dry-run --format simple

you get:

Story: transfer to cash account
As a savings account holder
  I want to transfer money from my savings account
  So that I can get cash easily from an ATM

Scenario: savings account is in credit
  Given my savings account balance is 100
  Given my cash account balance is 10
  When I transfer 20
  Then my savings account balance should be 80
  Then my cash account balance should be 30

Scenario: savings account is overdrawn
  Given my savings account balance is -20
  Given my cash account balance is 10
  When I transfer 20
  Then my savings account balance should be -20
  Then my cash account balance should be 10

This is being generated from the same Ruby code that runs the scenarios themselves.

Next steps

You can gem install rbehave or download the source. There is a sample application (Conway’s Game of Life) in progress in the source code that shows you some of the other features rbehave supports.

rbehave was designed to play nice with other frameworks. In particular, the “world” that each scenario runs in is a module that can be mixed into any object, so you could easily use rbehave with a Rails IntegrationTest or incorporate it into your existing acceptance testing framework. It is also possible, and in fact encouraged, to use rspec in your Outcomes (balance.should == 30).

If you’re interested in using or developing rbehave, please join the mailing lists and let me know how you get on. As a parting thought, rbehave is totally compatible with jruby, so you could start writing your Java acceptance criteria in Ruby and running them in rbehave.

A number of people helped me get rbehave off the ground. In particular I have to thank Niclas Nilsson for kick-starting the whole thing, David Chelimsky (rspec lead) for his sound advice and for adding describe/it to the rspec core, Liz Keogh (jbehave lead) for demanding that steps should take parameters and not taking no for an answer. Also PragDave ran an inspiring meta-programming workshop at QCon that gave me the courage to try this stuff.

41 comments

  1. BDD Activity…

    There’s been a flurry of BDD activity in the last week or so. Via evolving my understanding of behavior driven development (bdd) comes Pete Williams’ Mocking and R-S-P-E-C 4-ME.
    There’s also Goodbye TDD and TDD and Other Thinking Tool…

  2. [...] North also tackles the problem similarly with his just announced rbehave framework. I just saw this for the first time yesterday, so I haven’t had a chance to really [...]

  3. I like the syntax but the caps are kinda jarring…

    1. Hi Charles.

      Unfortunately matz beat me to it by reserving when and then! So the choice was _when or When, and I felt the underscores were more distracting than the caps.

  4. This looks great! Is there any reason why rbehave can’t be used instead of rspec? There seems to be a good deal of overlap in what they are trying to achieve.

    1. Hi Peter.

      rspec and rbehave are designed to work at different levels of granularity. At the TDD level (“examples”), there is very little reuse of givens, events and outcomes, so the rbehave syntax gets unwieldy. The occasional reuse of setup is handled in rspec with before(:each) do ... end. On the other hand, rspec isn’t geared up for scenario-level behaviour because of the amount of reuse it requires. Also, mocking is intrinsically baked into rspec (whether you use its own framework or something like Mocha), whereas it’s more of a side issue with rbehave.

      I find that in my rspec examples I use @#given@, @#when@, @#then@ as comments to structure the examples (take a look at some of the rspec examples in the rbehave codebase), so the intent is similar but the examples are easier to read.

  5. [...] the former, but I’ll confess the latter is usually gibberish to me. So what joy it is to read a blog that describes code, but is eminently understandable. Behaviour Driven Design that Dan talks about not only makes [...]

  6. Am I the only one thinking about literate programming when I see this? Like the pingback says: Something that is eminently understandable.

    I am wondering, though: When you program like this, do you write separate rspec examples when you have Pending Scenarios, or do you jump directly to making the Scenarios Pass?

    1. I use the scenarios to identify the “outermost” domain objects and services – the ones that interact with the real world. Then I use rspec and mocha to implement those, which drives out dependent objects (secondary domain objects, repositories, services, etc).

      All the while I’m focused on trying to get the current pending scenario to pass, which stops me adding features I just thought of. And believe me, I need that! It also helps me know when I’m done.

  7. My colleague Joe Ocampo (agilejoe) has spawned something similar in C# after reading this post.

    The post is here: http://www.lostechies.com/blogs/joe_ocampo/archive/2007/06/18/rbehave-with-nunit.aspx

    Keep’em coming Dan. Your posts and influence are awesome.

    Thanks.

    1. Hi Jason.

      Thanks for the feedback :) I’m really impressed with Joe’s C# version. I can’t believe how quickly he followed up my post. Quite amazing!

  8. Hi Dan,
    I’m having trouble with the following story code:

    require 'rubygems'
    require 'rbehave'
    require 'spec'
    require 'madlibs_game'
    
    Story "The Madlibs Game",
    %(As a player, I am prompted for placeholders by the interface and the story is then read back to me) do
    
      Scenario "only one placeholder" do
        Given "the story with placeholders is", "Our favourite language is ((a gemstone))" do |story|
          @story = story
        end
        When "I start the game" do
          @game = MadlibsGame.new(@story)
        end
        Then "the game prompts me to enter", "a gemstone" do |expected_prompt|
          @game.prompt.should match(/expected_prompt/)
        end
      end
    end
    

    The “match” syntax from rspec is not getting picked up. Am I doing something wrong or is there no way to combine rbehave and rspec in such a way?

    Thanks,

    1. Hi Jake.

      I looked into this and realised I’ve only been using should ==. It turns out rspec is smarter than I thought about when it makes the matchers available. I’ll add the matchers into rbehave, but in the meantime you can use them by putting this snippet in a helper.rb or somewhere ahead of your Story definition:

      module RBehave::World
        include Spec::Matchers
      end
      

      Thanks, Dan

    2. I’ve released a new version (0.3.0) that should fix this.

  9. [...] OK I was bored yesterday and I decided to update what I had worked on the other day from Dan North's post on rbehave. [...]

  10. [...] OK I was bored yesterday and I decided to update what I had worked on the other day from Dan North’s post on rbehave. [...]

  11. [...] you prefer behaviour-driven development (BDD) to TDD Dan North has recently developed rbehave for Ruby: “Inspired by [rspec], I wanted to find a simple and elegant way in Ruby to describe [...]

  12. Michael · ·

    Dan,

    Could you comment on how you integrate RBehave with Rails development, are there any hooks to use intermix of RSpec and RBehave specs and run ‘em with the single rake task?

    I just hope it is not somewhat yet to be developed :) Thanks.

    1. Hi Michael.

      Rails is definitely on the radar. The current roadmap is that we are integrating rbehave into rspec so you will have a single “full stack” BDD framework for Ruby. This will allow me to leverage all the good work the rspec team has done integratng with Rails.

      The next milestone will be that you can express Rails user stories using rbehave-style stories and scenarios rather than (or as well as) the rspec describe/it syntax.

      Watch this space – as soon as it’s integrated – even in a basic form whereby you can define stories and scenarios in rspec – I’ll announce something here.

      Thanks,
      Dan

  13. Good bye NUnit.Behave hello Behave#…

    Most of you who read my blog know that I have been working on NUnit.Behave which was meant to mirror…

  14. [...] surprised by the interest and chatter that I had with others after the talk, especially around rbehave. I wonder how the conversation would have turned out had I used JBehave examples [...]

  15. Architecture Chat 13 (or is 14?)…

  16. Dan Rolander · ·

    Will there be an html formatter soon for rbehave, or will the merge with rspec allow use of the rspec html formatter with rbehave?

    1. Hi Dan.

      We are working hard to integrate rbehave into rspec as a new Spec::Story module. As soon as that is complete (currently all examples are running but we only (!) have 99.6% coverage) I want to start leveraging all the cool reporting features of rspec.

      RBehave currently only has the rdoc-style documenter. We’ll need to write a new documenter for the story stuff, because the callback methods are different, but we should be able to factor out all the nice html layout features. It’s a top priority for me as soon as the integration work is done (which should be in the next few days).

  17. [...] of the neat things I saw at the Agile conference was a short demo of RSpec and RBehave. Intrigued, I did a quick search and found the .NET equivalents: Behave# and NSpec. Note: [...]

  18. Trying out Behave#…

    One of the neat things I saw at the Agile conference was a short demo of RSpec and RBehave . Intrigued,…

  19. [...] an unreleased and still-ironing-out-the-bugs state, the next release of RSpec will also come with rbehave merged into it, so it’s also possible to setup user stories to replace Rails’ own integration [...]

  20. Wow. I was recently wondering how to mimic rails integration testing with rspec and this looks like the solution! Can’t wait to see it integrated more into rspec and/or rails. Keep it up!

    1. Hi Justin.

      You might be interested in Pat Maddox’s recent post where he converts an IntegrationTest-style acceptance test into an rspec story. He’s written a really nice article.

      1. Thanks Dan! That’s a great article. Looks like the integration has already begun. Are the stories runnable with a rake task yet?

  21. [...] jalankan aplikasi yang kita bikin dengan cara usil, sambil direkam di screencast, lalu buatlah rbehave maupun rspec sesuai kelakuan si geek usil tadi ;-) tentu akan jauh lebih worth it daripada [...]

  22. [...] seen the light with RSpec and indeed, rbehave, I found myself wanting the same simple expressiveness in Java– accordingly, I began [...]

  23. In the example described above, for the Scenario “savings account is in credit”

    at the line “Then my savings account balance should be $80″
    I changed it to “Then my savings account balance should be $85″

    then both the scenarios fail and the result is as shown below

    Running 2 scenarios:
    FF

    2 scenarios: 0 succeeded, 2 failed, 0 pending

    FAILURES:
    1) transfer to cash account (savings account is in credit) FAILED
    Spec::Expectations::ExpectationNotMetError: expected: 85,
    got: 80 (using ==)
    e.rb:39:in `my savings account balance should be’
    e.rb:38
    e.rb:24

    2) transfer to cash account (savings account is overdrawn) FAILED
    RBehave::UnknownStepException: my cash account balance should be
    e.rb:51
    e.rb:24

    I expected the 2nd test to pass, and it is giving a Unknown Step Exception. Is this a bug ??

    I have one more suggestion. Is there any way we can make the scenario where we put in the logic as a more generalized one with appropirate ruby code, and keep the other set of scenarios as written by a business analyst, as separate set.

    1. Hi Venu.

      Thanks for your suggestions. I hope I can help.

      Since I wrote this article, RBehave has become the rspec story framework. The rspec team has put a lot of effort into making the story syntax cleaner (you can now specify scenarios as plain text!) and separating out the ruby code. You can also manage different groups of steps separately, which will allow you to do exactly what you are describing with separating out the detail of the scenarios.

      You should start using the rspec story framework because I won’t be making any more separate releases of rbehave.

  24. [...] (e.g. a Given step can run an equivalent Given method). The list is growing fast: JBehave, RBehave, NBehave, RSPec, Cucumber, and so on. At InneWorkings, we experimented for a long time with various [...]

  25. [...] Having said that, here is what I came up with. It’s heavily inspired by MSpec and RBehave. [...]

  26. [...] powerful. Here is what its authors say about NBehave: Based on Dan North’s initial vision of rbehave and utilizing the behavioral domain specific language (DSL)of Behavior Driven Design (BDD) we [...]

  27. [...] Dan North created rbehave which is the Story framework and he describes it as: [...]

  28. [...] Introducing rbehave [...]

Follow

Get every new post delivered to your Inbox.

Join 463 other followers

%d bloggers like this: