• Stars
    star
    972
  • Rank 47,098 (Top 1.0 %)
  • Language
    Ruby
  • Created about 13 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Gherkin extension for RSpec

Turnip

Join the chat at https://gitter.im/jnicklas/turnip

Test Code Climate

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/rspec

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 and support policy

1. Ruby

Supports Non-EOL Rubies:

  • Support Ruby 2.7 or higher
  • Does not support Ruby (or does not work) 2.6.X or earlier

2. RSpec

In accordance with the RSpec support policy #158 (comment)

  • Support RSpec 3.x the latest or one version before
  • Does not support two version before the latest or earlier
  • Does not work on RSpec 2 or earlier

Example If the latest version is 3.5.x:

  • Support 3.5.x
  • Support 3.4.x
  • Does not support (or does not work) 3.3.x or earlier

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 can define steps on any module:

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

You can now include this module in RSpec:

RSpec.configure { |c| c.include MonsterSteps }

Steps are implemented as regular Ruby methods under the hood, so you can use Ruby's normal inheritance chain to mix and match steps.

Before/After Hooks

Since Turnip runs atop RSpec, it can utilize RSpec's built-in before and after hooks. To run a hook for all features, specify a global hook with type set to :feature:

config.before(:type => :feature) do
  do_something
end
config.after(:type => :feature) do
  do_something_else
end

You can also limit this to a tag by specifying the tag in the argument to before or after:

config.before(:some_tag => true) do
  do_something
end

Global steps

Turnip has a special module called Turnip::Steps, which is automatically included in RSpec. If you add steps to this module, they are available in all your features. As a convenience, there is a shortcut to doing this, just call step in the global namespace like this:

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

Placeholders

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

Since steps are defined on modules, you can pick and choose which of them are available in which feature. This can be extremely useful if you have a large number of steps, and do not want them to potentially conflict.

If you had some scenarios which talk to the database directly, and some which go through a user interface, you could implement it as follows:

module InterfaceSteps
  step "I do it" do
    ...
  end
end

module DatabaseSteps
  step "I do it" do
    ...
  end
end

RSpec.configure do |config|
  config.include InterfaceSteps, :interface => true
  config.include DatabaseSteps, :database => true
end

Turnip turns tags into RSpec metadata, so you can use RSpec's conditional include feature to include these steps only for those scenarios tagged the appropriate way. So 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

Be careful though not to tag a feature with both @interface and @database in this example. Since steps use the Ruby inheritance chain, the step which is included last will "win", just like any other Ruby method. This might not be what you expect.

Since this pattern of creating a module and including it for a specific tag is very common, we have created a handy shortcut for it:

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

Check out features/alignment_steps.rb

for an example.

Where to place steps

Turnip automatically loads your spec_helper file. From there you can place your steps wherever you want, and load them however you like. For example, if you were to put your steps in spec/steps, you could load them like this:

Dir.glob("spec/steps/**/*steps.rb") { |f| load f, true }

Before loading your spec_helper, Turnip also tries to load a file called turnip_helper where you can setup anything specific to your turnip examples. You might find it beneficial to load your steps from this file so that they don't have to be loaded when you run your other tests.

If you use Turnip with rspec-rails, most configuration written to rails_helper.rb but not spec_helper.rb. So you should write to turnip_helper like this:

require 'rails_helper'

Then you can write configuration to rails_helper to load your steps.

Calling steps from other steps

Since steps are Ruby methods you can call them like other Ruby methods. However, since the step description likely contains spaces and other special characters, you will probably have to use send to call the step:

step "the value is :num" do |num|
  @value = num
end

step "the value is twice as much as :num" do |num|
  send "the value is :num", num * 2
end

If you use the second step, it will call into the first step, sending in the doubled value.

Sometimes you will want to call the step just like you would from your feature file, in that case you can use the step method:

step "the value is :num" do |num|
  @value = num
end

step "the value is the magic number" do
  step "the value is 3"
end

Methods as steps

You can mark an existing method as a step. This will make it available in your Turnip features. For example:

module MonsterSteps
  def create_monster(name)
    @monster = Monster.new(:name => name)
  end
  step :create_monster, "there is a monster called :name"
end

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).

Note that custom placeholders can capture several words separated by spaces and without surrounding quotes, e.g.:

step 'there is :monster in the loft' do |monster|
  # call 'Given there is green furry monster in the loft',
  # :monster will capture 'green furry moster'
end

E.g. Common should / should not steps:

step 'I :whether_to see :text' do |positive, text|
  expectation = positive ? :to : :not_to
  expect(page.body).send expectation, eq(text)
end

placeholder :whether_to do
  match /should not/ do
    false
  end

  match /should/ do
    true
  end
end

Then, it is possible to call the following steps:

Then I should see 'Danger! Monsters ahead!'
 And I should not see 'Game over!'

You can also define custom placeholder without specific regexp, that matches the same value of the default placeholder like this:

placeholder :monster_name do
  default do |name|
    Monster.find_by!(name: name)
  end
end

Table Steps

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 objects.

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

or the equivalent:

step "there are the following monsters:" do |table|
  @monsters = {}
  table.rows.each do |(name, hp)|
    @monsters[name] = hp.to_i
  end
end

Unimplemented steps

Turnip mark a scenario as pending when steps in the scenario is not implemented. If you sets raise_error_for_unimplemented_steps as true, turnip will mark a scenario as fail.

It defaults to false, you can change it by adding following configuration to spec/turnip_helper.rb:

RSpec.configure do |config|
  config.raise_error_for_unimplemented_steps = true
end

Substitution in Scenario Outlines

You would be able to use substitution that can be used to DocString and Table arguments in Scenario Outline like Cucumber:

Scenario Outline: Email confirmation
  Given I have a user account with my name "Jojo Binks"
  When an Admin grants me <Role> rights
  Then I should receive an email with the body:
    """
    Dear Jojo Binks,
    You have been granted <Role> rights.  You are <details>. Please be responsible.
    -The Admins
    """
  Examples:
    |  Role     | details                                         |
    |  Manager  | now able to manage your employee accounts       |
    |  Admin    | able to manage any user account on the system   |

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 => :feature metadata, so that Capybara is included and also any other extensions you might want to add.

RSpec custom formatters

Turnip sends notifications to the RSpec reporter about step progress. You can listen for these by registering your formatter class with the following notifications:

class MyFormatter
  RSpec::Core::Formatters.register self, :step_started, :step_passed, :step_failed, :step_pending
  
  def step_passed(step)
    puts "Starting step: #{step.text}"
  end

  # …
end

License

(The MIT License)

Copyright (c) 2011-2012 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.

More Repositories

1

bistro_car

This project is obsolete. Use the asset pipeline now built into Rails instead.
Ruby
100
star
2

templater

RubiGen alternative Generator thingy
Ruby
49
star
3

memoit

Memoize method in Ruby
Ruby
47
star
4

feed_me

A tiny parser for RSS and Atom feeds built on Nokogiri.
Ruby
42
star
5

capybara_table

Capybara selectors and matchers for working with HTML tables
Ruby
41
star
6

uploadcolumn

UploadColumn is no longer maintained, check out CarrierWave for an alternative
Ruby
31
star
7

carrierwave-example-app

An example app showcasing some features of the Ruby file upload library CarrierWave (http://github.com/jnicklas/carrierwave)
Ruby
30
star
8

contextually

Nice user contexts in your RSpec-Rails controller specs
Ruby
21
star
9

graphers

Very very experimental implementation of a GraphQL server in Rust
Rust
19
star
10

partners

Switch between different git users
Rust
15
star
11

liability

Asset packaging Rack middleware
Ruby
12
star
12

eb_nested_set

This pretentious little plugin aims to make nested_set cool again
Ruby
10
star
13

expgen

Generate random strings from regular expressions
Ruby
9
star
14

taylor

Auto magical fixture replacement.
Ruby
8
star
15

xproc

Awesome short procs for Ruby
Ruby
7
star
16

courgette

Browse your cucumber features from within your Rails app with Courgette, a Rails engine.
Ruby
7
star
17

rspec-concurrent

Terrible hack to run RSpec concurrently with Celluloid
Ruby
7
star
18

master_class

Repo for the testing master class I am holding at RubyC
Ruby
6
star
19

rorem

Rorem is a random data generator
Ruby
6
star
20

advanced_capybara

A repo containing the example app and slides of my advanced capybara talk
Ruby
6
star
21

prison

Warden based authentication engine for Rails
Ruby
5
star
22

earley

Rust
4
star
23

mini-effection

Experimental reimplementation of Effection in Typescript
TypeScript
4
star
24

ffmpeg_test

Testing some ffmpeg stuff
C
4
star
25

celluloid-stomp

Evented STOMP parser
Ruby
3
star
26

capybara_heroku

A small skeleton to run the capybara test app on Heroku
Ruby
3
star
27

coffee_compiler

Compile CoffeeScript from Ruby without shelling out
Ruby
3
star
28

chianna

Component based Web Development Framework
Ruby
3
star
29

gibberish_attributes

An extension that adds attributes in different languages to Gibberish, ohh, the fun!
Ruby
3
star
30

standard_error

Rust
3
star
31

build_compile

Helper crate for build scripts which compile to Rust
Rust
3
star
32

rrandom

Generate random data
Ruby
2
star
33

elabs-profile

Elabs awesome pairing station setup
Shell
2
star
34

taskomaly

a ruby api for tasko @ taskodone.com
Ruby
2
star
35

confiture

Configuration Generator
Ruby
2
star
36

i18n_moneta

A Moneta Backend for Rails i18n
Ruby
2
star
37

micro_hooks

A tiny, tiny hooks framework. Really, very tiny.
Ruby
2
star
38

with_position

A Rust extension trait for iterators which yields the position of the item in the iteration
Rust
2
star
39

ikebana_example

Evergreen example repo
Ruby
2
star
40

lilliput

A very simple blogging engine
Ruby
2
star
41

fooma

A calorie recipe thingy
2
star
42

attveta

Foundation website
HTML
1
star
43

forky

Silly template forking thing
Ruby
1
star
44

rollup-cleanup-demo

Reproduction for resource cleanup bug in rollup
JavaScript
1
star
45

dotfiles

Vim Script
1
star
46

testing_ikebana

Slides for my presentation at SWDC2011
JavaScript
1
star
47

sauce_tunnel

Ruby gem to establish tunnel connection to SauceLabs via their `sc` CLI
Ruby
1
star
48

river-stream

Stream primitives
1
star
49

gocanvas

A jquery like library for the HTML5 "canvas" API
JavaScript
1
star
50

horrible

A Ruby webframework built on Rack and Fiber abuse.
Ruby
1
star