Turnip

Build Status

Turnip is a Gherkin extension for RSpec. It allows you to write tests in Gherkin and run them through your RSpec environment. Basically you can write cucumber features in RSpec.

Installation

Install the gem

gem install turnip

Or add it to your Gemfile and run bundle.

group :test do
  gem "turnip"
end

Now edit the .rspec file in your project directory (create it if doesn't exist), and add the following line:

-r turnip

Development

  • Source hosted at GitHub.
  • Please direct questions, discussion or problems to the mailing list. Please do not open an issue on GitHub if you have a question.
  • If you found a reproducible bug, open a GitHub Issue to submit a bug report.
  • Please do not contact any of the maintainers directly, unless you have found a security related issue.

Pull requests are very welcome (and even better than bug reports)! Please create a topic branch for every separate change you make.

Compatibility

Turnip does not work on Ruby 1.8.X.

Usage

Add a feature file anywhere in your spec directory:

# spec/acceptance/attack_monster.feature
Feature: Attacking a monster
  Background:
    Given there is a monster

  Scenario: attack the monster
    When I attack it
    Then it should die

Now you can run it just like you would run any other rspec spec:

rspec spec/acceptance/attack_monster.feature

It will automatically be run if you run all your specs with rake spec or rspec spec.

Yes, that's really it.

Defining steps

You might want to define some steps. Just as in cucumber, your step files should be named [something]_steps.rb. All files ending in *steps.rb will be automatically required if they are under the Turnip step directory.

The default step directory for Turnip is spec/. You can override this in your spec_helper by setting Turnip::Config.step_dirs. For example:

# spec/spec_helper.rb
RSpec.configure do |config|
  Turnip::Config.step_dirs = 'examples'
  Turnip::StepLoader.load_steps
end

This would set the Turnip step dirs to examples/ and automatically load all *steps.rb files anywhere under that directory.

The steps you define in your step files can be global or they can be scoped to certain features (or scenarios)...

Global steps

Global steps can be used by any feature/scenario you write since they are unscoped. The names must be unique across all step files in the global namespace.

Define them in your step file like this:

step "there is a monster" do
  @monster = Monster.new
end

Note that unlike Cucumber, Turnip does not support regexps in step definitions. You can however use placeholders in your step definitions, like this:

step "there is a monster called :name" do |name|
  @monster = Monster.new(name)
end

You can now put values in this placeholder, either quoted or not:

Given there is a monster called Jonas
And there is a monster called "Jonas Nicklas"

You can also specify alternative words and optional parts of words, like this:

step "there is/are :count monster(s)" do |count|
  @monsters = Array.new(count) { Monster.new }
end

That will match both "there is X monster" or "there are X monsters".

You can also define custom step placeholders. More on that later.

Scoped steps

Scoped steps help you to organize steps that are specific to certain features or scenarios. They only need to be unique within the scopes being used by the running scenario.

To define scoped steps use steps_for:

steps_for :interface do
  step "I do it" do
    ...
  end
end

steps_for :database do
  step "I do it" do
    ...
  end
end

Even though the step is named the same, you can now use it in your feature files like so:

@interface
Scenario: do it through the interface

@database
Scenario: do it through the database

Note that this would still cause an error if you tagged a Scenario with both @interface and @database at the same time.

Scoped steps are really just Ruby modules under the covers so you can do anything you'd normally want to do including defining helper/utility methods and variables. Check out features/alignment_steps.rb and features/evil_steps.rb for basic examples.

Reusing steps

When using scoped steps in Turnip, you can tell it to also include steps defined in another steps_for block. The syntax for that is use_steps:

# dragon_steps.rb
steps_for :dragon do
  use_steps :knight

  attr_accessor :dragon

  def dragon_attack
    dragon * 10
  end

  step "there is a dragon" do
    self.dragon = 1
  end

  step "the dragon attacks the knight" do
    knight.attacked_for(dragon_attack)
  end
end

# red_dragon_steps.rb
steps_for :red_dragon do
  use_steps :dragon

  attr_accessor :red_dragon

  def dragon_attack
    attack = super
    if red_dragon
      attack + 15
    else
      attack
    end
  end

  step "the dragon breathes fire" do
    self.red_dragon = 1
  end
end

In this example we are making full use of Ruby's modules including using super to call the included module's version of dragon_attack, for example with the following feature file:

Feature: Red Dragons are deadly

  @dragon
  Scenario:
    Given there is a dragon
    And there is a knight
    When the dragon attacks the knight
    Then the knight is alive

  @red_dragon
  Scenario:
    Given there is a dragon
    And the dragon breathes fire
    And there is a knight
    When the dragon attacks the knight
    Then the knight is dead

Auto-included steps

By default, Turnip will automatically make available any steps defined in a steps_for block with the same name as the feature file being run. For example, given this step file:

# user_signup_steps.rb
steps_for :user_signup do
  step "I am on the homepage" do
    ...
  end

  step "I signup with valid info" do
    ...
  end

  step "I should see a welcome message" do
  end
end

Then the following feature file would run just fine even though we did not explicitly tag it with @user_signup.

# user_signup.feature
Feature: A user can signup
  Scenario: with email address
    Given I am on the homepage
    When I signup with valid info
    Then I should see a welcome message

Note that the steps_for :user_signup did not technically have to appear in the user_signup_steps.rb file; it could have been located in any steps.rb file that was autoloaded by Turnip.

This feature can be turned off using the Turnip::Config.autotag_features option if desired.

Custom step placeholders

Do you want to be more specific in what to match in your step placeholders? Do you find it bothersome to have to constantly cast them to the correct type? Turnip supports custom placeholders to solve both problems, like this:

step "there are :count monsters" do |count|
  count.times { Monster.new(name) }
end

placeholder :count do
  match /\d+/ do |count|
    count.to_i
  end

  match /no/ do
    0
  end
end

You would now be able to use these steps like this:

Given there are 4 monsters
Given there are no monsters

Placeholders can extract matches from the regular expressions as well. For example:

placeholder :monster do
  match /(blue|green|red) (furry|bald) monster/ do |color, hair|
    Monster.new(color, hair)
  end
end

These regular expressions must not use anchors, e.g. ^ or $. They may not contain named capture groups, e.g. (?<color>blue|green).

Using with Capybara

Just require turnip/capybara in your spec_helper. You can now use the same tags you'd use in Cucumber to switch between drivers e.g. @javascript or @selenium. Your Turnip features will also be run with the :type => :request metadata, so that Capybara is included and also any other extensions you might want to add.

License

(The MIT License)

Copyright (c) 2011 Jonas Nicklas

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.