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.
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
- 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.
Turnip does not work on Ruby 1.8.X.
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.
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 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 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.
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
You can also call steps from other steps. This is done by just calling step "name_of_the_step"
, so for instance if you have:
step "a random step" do
@value = 1
end
step "calling a step" do
step "a random step"
@value += 1
end
Now if you use the step calling a step
in any Scenario, then the value of
@value
will be 2 afterwards as it first executes the code defined for the step
a random step
. You can think of it as a simple method call.
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.
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)
.
Turnip also supports steps that take a table as a parameter similar to Cucumber:
Scenario: This is a feature with a table
Given there are the following monsters:
| Name | Hitpoints |
| Blaaarg | 23 |
| Moorg | 12 |
Then "Blaaarg" should have 23 hitpoints
And "Moorg" should have 12 hitpoints
The table is a Turnip::Table
object which works in much the same way as Cucumber's
Cucumber::Ast::Table
obects.
E.g. converting the Turnip::Table
to an array of hashes:
step "there are the following monsters:" do |table|
@monsters = {}
table.hashes.each do |hash|
@monsters[hash['Name']] = hash['Hitpoints'].to_i
end
end
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.
(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.