InfoQ

InfoQ

News

My Bookmarks

Login or Register to enable bookmarks for unlimited time.

The content has been bookmarked!

There was an error bookmarking this content! Please retry.

RSpec Adds Eagerly-Awaited RBehave Functionality for Integration Testing

Posted by Sean Miller on Oct 31, 2007

Sections
Process & Practices,
Architecture & Design,
Development
Topics
Ruby ,
Delivering Quality ,
Customers & Requirements ,
Agile
Tags
Testing ,
BDD ,
RSpec ,
RBehave
RSpec is a Behaviour-Driven Development (BDD) acceptance testing framework written for Ruby and also usable (since it can run under JRuby) for Java. Historically it has needed an add-on extension to handle story-level integration tests, but David Chelimsky has now added a Plain Text Story Runner directly into RSpec, incorporating RBehave functionality.

RSpec provides a mechanism by which developers can take acceptance specifications from the business and turn them into readable and executable examples that can serve as documentation, tests, and business-readable build reports. While RSpec is useful for unit-level tests, it did not support story-level integration tests out of the box. Dan North built a separate extension, RBehave, to describe behaviour at the story level, as a series of steps of the form Given ... With ... Then ... . (North first described this pattern for capturing story requirements in the context of JBehave.)

David Chelimsky has now incorporated into the RSpec trunk a Plain Text Story Runner that gives RSpec the functionality of RBehave, as described in his blog.

So, North's classic RBehave example:

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

can become, in the new RSpec, a Ruby file to define the available steps:
 
class AccountSteps < Spec::Story::StepGroup
  steps do |define|
    define.given("my savings account balance is $balance") do |balance|
      @savings_account = Account.new(balance.to_f)
    end

    define.given("my cash account balance is $balance" do |balance|
      @cash_account = Account.new(balance.to_f)
    end

    define.then("my savings account balance should be $expected_amount" do |expected_amount|
      @savings_account.balance.should == expected_amount.to_f
    end

    define.then("my cash account balance should be $expected_amount" do |expected_amount|
      @cash_account.balance.should == expected_amount.to_f
    end
  end
end

steps = AccountSteps.new do |define|
  define.when("I transfer $amount") do |amount|
    @savings_account.transfer_to(@cash_account, amount.to_f)
  end
end

a plain text file that defines the story behaviour in terms of those steps:

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
 
and a Ruby file to glue them together and run the story:

require 'spec'
require 'path/to/your/library/files'
require 'path/to/file/that/defines/account_steps.rb'

# assumes the other story file is named the same as this file minus ".rb"
runner = Spec::Story::Runner::PlainTextStoryRunner.new(File.expand_path(__FILE__).gsub(".rb",""))
runner.steps << AccountSteps.new
runner.run

The wording of the steps in the plain text file has to match the steps defined in the StepGroup, which could become unwieldy with large numbers of steps. To make it easier, Aslak Hellesøy is working on a browser-based editor, with autocompletion for steps and in-place editing of parameters.

  • This article is part of a featured topic series on Agile

Related Sponsor

In today’s hyper-competitive world, later may be too late to adopt Agile development and this Roadmap for Success will help you get started. Download "Agile Development: A Manager's Roadmap for Success" now!

Interested to Try This by Geoffrey Wiseman Posted
  1. Back to top

    Interested to Try This

    by Geoffrey Wiseman

    Looking forward to trying this; I'm still not sure if I prefer the mixed-code-and-text model that existed before (earlier versions of Story Runner, and possibly RBehave before that), or the separation achieved here.

    If the steps end up being relatively re-usable and you are able to get business users (on-site customers, program managers, whatever) to write stories this new format seems well-suited for that.

    OTOH, if developers are end up writing one story in two files with not a lot of re-use, it seems like this structure will just get in the way.

    Either way, curious to try it 'for real' and form an opinion.

Educational Content

Beauty Is in the Eye of the Beholder

Alex Papadimoulis discusses ugly code, where it comes from, how to avoid it, and how to get rid of it.

Architecting Visa for Massive Scale and Continuous Innovation

John Davies examines Visa’s architecture and shows how enterprises have architected complex integrations incorporating Hadoop, memcached, Ruby on Rails, and others to deliver innovative solutions.

Max Protect: Scalability and Caching at ESPN.com

Sean Comerford unveils ESPN.com’s architecture, what components are used and why, and the current changes the website goes through.

The Seven Deadly Sins of Enterprise Agile Adoption

Are there repeated patterns of failure on Enterprise Agile Enablement efforts? Sanjiv and Arlen discuss Seven Deadly Sins to avoid when adopting Agile in an enterprise.

Questions for an Enterprise Architect

Erik Dörnenburg answers: What is Enterprise and Evolutionary Architecture?, discussing 4 issues: Turning strategy into execution, Ensuring conformance, Where do the architects sit? Buying or building?

Wrap Your SQL Head Around Riak MapReduce

Sean Cribbs explains what Map-Reduce and Riak are, why and how to use Map-Reduce with Riak, and how to convert SQL queries into their Map-Reduce equivalents.

Polyglot Persistence for Java Developers - Moving Out of the Relational Comfort Zone

Chris Richardson shows how he ported a relational database to three NoSQL data stores: Redis, Cassandra and MongoDB.

The Golden Circle – Why How What

Jean Tabaka challenges the audience to reflect on what Agile practices they are employing, how they are using them, ending with the questions “Why have their organization chosen to go Agile?