{"id":13416425,"url":"https://github.com/jnicklas/turnip","last_synced_at":"2025-05-14T09:11:09.323Z","repository":{"id":46115296,"uuid":"2621662","full_name":"jnicklas/turnip","owner":"jnicklas","description":"Gherkin extension for RSpec","archived":false,"fork":false,"pushed_at":"2024-08-09T09:04:29.000Z","size":390,"stargazers_count":976,"open_issues_count":11,"forks_count":111,"subscribers_count":31,"default_branch":"master","last_synced_at":"2025-05-11T11:37:41.181Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jnicklas.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2011-10-21T16:49:56.000Z","updated_at":"2025-03-24T22:20:54.000Z","dependencies_parsed_at":"2023-01-21T22:02:34.488Z","dependency_job_id":"239cbbfe-0ed1-4a50-ae34-5f6cc09911d3","html_url":"https://github.com/jnicklas/turnip","commit_stats":{"total_commits":355,"total_committers":62,"mean_commits":5.725806451612903,"dds":0.6957746478873239,"last_synced_commit":"1311e6c50d38148fac5bc0fed6866cfcb6e2fa4c"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jnicklas%2Fturnip","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jnicklas%2Fturnip/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jnicklas%2Fturnip/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jnicklas%2Fturnip/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jnicklas","download_url":"https://codeload.github.com/jnicklas/turnip/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254103517,"owners_count":22015281,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T21:00:58.639Z","updated_at":"2025-05-14T09:11:04.313Z","avatar_url":"https://github.com/jnicklas.png","language":"Ruby","readme":"# Turnip\n\n[![Join the chat at https://gitter.im/jnicklas/turnip](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jnicklas/turnip?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n![Test](https://github.com/jnicklas/turnip/workflows/Test/badge.svg)\n[![Code Climate](https://codeclimate.com/github/jnicklas/turnip.png)](https://codeclimate.com/github/jnicklas/turnip)\n\nTurnip is a [Gherkin](https://github.com/cucumber/cucumber/wiki/Gherkin)\nextension for RSpec. It allows you to write tests in Gherkin and run them\nthrough your RSpec environment. Basically you can write cucumber features in\nRSpec.\n\n## Installation\n\nInstall the gem\n\n```\ngem install turnip\n```\n\nOr add it to your Gemfile and run `bundle`.\n\n``` ruby\ngroup :test do\n  gem \"turnip\"\nend\n```\n\nNow edit the `.rspec` file in your project directory (create it if doesn't\nexist), and add the following line:\n\n```\n-r turnip/rspec\n```\n\n## Development\n\n* Source hosted at [GitHub](http://github.com/jnicklas/turnip).\n* Please direct questions, discussion or problems to the [mailing list](http://groups.google.com/group/ruby-turnip).\n  Please do not open an issue on GitHub if you have a question.\n* If you found a reproducible bug, open a [GitHub Issue](http://github.com/jnicklas/turnip/issues) to submit a bug report.\n* Please do not contact any of the maintainers directly, unless you have found a security related issue.\n\nPull requests are very welcome (and even better than bug reports)!\nPlease create a topic branch for every separate change you make.\n\n## Compatibility and support policy\n\n### 1. Ruby\n\nSupports Non-EOL Rubies:\n\n- Support Ruby 2.7 or higher\n- Does not support Ruby (or does not work) 2.6.X or earlier\n\n### 2. RSpec\n\nIn accordance with the RSpec support policy https://github.com/jnicklas/turnip/issues/158#issuecomment-119049054\n\n- Support RSpec 3.x **the latest or one version before**\n- Does not support **two version before the latest or earlier**\n- Does not work on **RSpec 2 or earlier**\n\nExample `If the latest version is 3.5.x`:\n\n- Support `3.5.x`\n- Support `3.4.x`\n- Does not support (or does not work) `3.3.x` or earlier\n\n## Usage\n\nAdd a feature file anywhere in your `spec` directory:\n\n``` cucumber\n# spec/acceptance/attack_monster.feature\nFeature: Attacking a monster\n  Background:\n    Given there is a monster\n\n  Scenario: attack the monster\n    When I attack it\n    Then it should die\n```\n\nNow you can run it just like you would run any other rspec spec:\n\n```\nrspec spec/acceptance/attack_monster.feature\n```\n\nIt will automatically be run if you run all your specs with `rake spec` or\n`rspec spec`.\n\nYes, that's really it.\n\n## Defining steps\n\nYou can define steps on any module:\n\n``` ruby\nmodule MonsterSteps\n  step \"there is a monster\" do\n    @monster = Monster.new\n  end\nend\n```\n\nYou can now include this module in RSpec:\n\n``` ruby\nRSpec.configure { |c| c.include MonsterSteps }\n```\n\nSteps are implemented as regular Ruby methods under the hood, so you can\nuse Ruby's normal inheritance chain to mix and match steps.\n\n### Before/After Hooks\n\nSince Turnip runs atop RSpec, it can utilize RSpec's built-in before and after\nhooks. To run a hook for all features, specify a global hook with `type` set\nto `:feature`:\n\n```ruby\nconfig.before(:type =\u003e :feature) do\n  do_something\nend\nconfig.after(:type =\u003e :feature) do\n  do_something_else\nend\n```\n\nYou can also limit this to a tag by specifying the tag in the argument to\n`before` or `after`:\n\n```ruby\nconfig.before(:some_tag =\u003e true) do\n  do_something\nend\n```\n\n### Global steps\n\nTurnip has a special module called `Turnip::Steps`, which is automatically\nincluded in RSpec. If you add steps to this module, they are available in all\nyour features. As a convenience, there is a shortcut to doing this, just call\n`step` in the global namespace like this:\n\n``` ruby\nstep \"there is a monster\" do\n  @monster = Monster.new\nend\n```\n\n### Placeholders\n\nNote that unlike Cucumber, Turnip does not support regexps in step definitions.\nYou can however use placeholders in your step definitions, like this:\n\n``` ruby\nstep \"there is a monster called :name\" do |name|\n  @monster = Monster.new(name)\nend\n```\n\nYou can now put values in this placeholder, either quoted or not:\n\n``` cucumber\nGiven there is a monster called Jonas\nAnd there is a monster called \"Jonas Nicklas\"\n```\n\nYou can also specify alternative words and optional parts of words, like this:\n\n``` ruby\nstep \"there is/are :count monster(s)\" do |count|\n  @monsters = Array.new(count) { Monster.new }\nend\n```\n\nThat will match both \"there is X monster\" or \"there are X monsters\".\n\nYou can also define custom step placeholders. More on that later.\n\n### Scoped steps\n\nSince steps are defined on modules, you can pick and choose which of them are\navailable in which feature. This can be extremely useful if you have a large\nnumber of steps, and do not want them to potentially conflict.\n\nIf you had some scenarios which talk to the database directly, and some which\ngo through a user interface, you could implement it as follows:\n\n``` ruby\nmodule InterfaceSteps\n  step \"I do it\" do\n    ...\n  end\nend\n\nmodule DatabaseSteps\n  step \"I do it\" do\n    ...\n  end\nend\n\nRSpec.configure do |config|\n  config.include InterfaceSteps, :interface =\u003e true\n  config.include DatabaseSteps, :database =\u003e true\nend\n```\n\nTurnip turns tags into RSpec metadata, so you can use RSpec's conditional\ninclude feature to include these steps only for those scenarios tagged the\nappropriate way. So even though the step is named the same, you can now use it\nin your feature files like so:\n\n``` cucumber\n@interface\nScenario: do it through the interface\n\n@database\nScenario: do it through the database\n```\n\nBe careful though not to tag a feature with both `@interface` and `@database`\nin this example. Since steps use the Ruby inheritance chain, the step which is\nincluded last will \"win\", just like any other Ruby method. This might not be\nwhat you expect.\n\nSince this pattern of creating a module and including it for a specific tag\nis very common, we have created a handy shortcut for it:\n\n``` ruby\nsteps_for :interface do\n  step \"I do it\" do\n    ...\n  end\nend\n```\n\nCheck out [features/alignment_steps.rb](https://github.com/jnicklas/turnip/blob/master/examples/steps/alignment_steps.rb)\n\nfor an example.\n\n### Where to place steps\n\nTurnip automatically loads your `spec_helper` file. From there you can place\nyour steps wherever you want, and load them however you like. For example, if\nyou were to put your steps in `spec/steps`, you could load them like this:\n\n``` ruby\nDir.glob(\"spec/steps/**/*steps.rb\") { |f| load f, true }\n```\n\nBefore loading your `spec_helper`, Turnip also tries to load a file called\n`turnip_helper` where you can setup anything specific to your turnip examples.\nYou might find it beneficial to load your steps from this file so that they\ndon't have to be loaded when you run your other tests.\n\nIf you use Turnip with [rspec-rails](https://github.com/rspec/rspec-rails), most configuration written to `rails_helper.rb` but not `spec_helper.rb`. So you should write to `turnip_helper` like this:\n\n```ruby\nrequire 'rails_helper'\n```\n\nThen you can write configuration to `rails_helper` to load your steps.\n\n### Calling steps from other steps\n\nSince steps are Ruby methods you can call them like other Ruby methods.\nHowever, since the step description likely contains spaces and other special\ncharacters, you will probably have to use `send` to call the step:\n\n``` ruby\nstep \"the value is :num\" do |num|\n  @value = num\nend\n\nstep \"the value is twice as much as :num\" do |num|\n  send \"the value is :num\", num * 2\nend\n```\n\nIf you use the second step, it will call into the first step, sending in the\ndoubled value.\n\nSometimes you will want to call the step just like you would from your feature\nfile, in that case you can use the `step` method:\n\n``` ruby\nstep \"the value is :num\" do |num|\n  @value = num\nend\n\nstep \"the value is the magic number\" do\n  step \"the value is 3\"\nend\n```\n\n### Methods as steps\n\nYou can mark an existing method as a step. This will make it available in your\nTurnip features. For example:\n\n``` ruby\nmodule MonsterSteps\n  def create_monster(name)\n    @monster = Monster.new(:name =\u003e name)\n  end\n  step :create_monster, \"there is a monster called :name\"\nend\n```\n\n## Custom step placeholders\n\nDo you want to be more specific in what to match in your step placeholders? Do\nyou find it bothersome to have to constantly cast them to the correct type?\nTurnip supports custom placeholders to solve both problems, like this:\n\n``` ruby\nstep \"there are :count monsters\" do |count|\n  count.times { Monster.new(name) }\nend\n\nplaceholder :count do\n  match /\\d+/ do |count|\n    count.to_i\n  end\n\n  match /no/ do\n    0\n  end\nend\n```\n\nYou would now be able to use these steps like this:\n\n``` cucumber\nGiven there are 4 monsters\nGiven there are no monsters\n```\n\nPlaceholders can extract matches from the regular expressions as well. For\nexample:\n\n``` ruby\nplaceholder :monster do\n  match /(blue|green|red) (furry|bald) monster/ do |color, hair|\n    Monster.new(color, hair)\n  end\nend\n```\n\nThese regular expressions must not use anchors, e.g. `^` or `$`. They may not\ncontain named capture groups, e.g. `(?\u003ccolor\u003eblue|green)`.\n\nNote that custom placeholders can capture several words separated by spaces and without surrounding quotes, e.g.:\n\n```ruby\n\nstep 'there is :monster in the loft' do |monster|\n  # call 'Given there is green furry monster in the loft',\n  # :monster will capture 'green furry moster'\nend\n```\n\nE.g. Common `should` / `should not` steps:\n\n```ruby\nstep 'I :whether_to see :text' do |positive, text|\n  expectation = positive ? :to : :not_to\n  expect(page.body).send expectation, eq(text)\nend\n\nplaceholder :whether_to do\n  match /should not/ do\n    false\n  end\n\n  match /should/ do\n    true\n  end\nend\n```\n\nThen, it is possible to call the following steps:\n\n```feature\nThen I should see 'Danger! Monsters ahead!'\n And I should not see 'Game over!'\n```\n\nYou can also define custom placeholder without specific regexp, that matches the same value of the default placeholder like this:\n\n```ruby\nplaceholder :monster_name do\n  default do |name|\n    Monster.find_by!(name: name)\n  end\nend\n```\n\n## Table Steps\n\nTurnip also supports steps that take a table as a parameter similar to Cucumber:\n\n``` cucumber\nScenario: This is a feature with a table\n  Given there are the following monsters:\n    | Name    | Hitpoints |\n    | Blaaarg | 23        |\n    | Moorg   | 12        |\n  Then \"Blaaarg\" should have 23 hitpoints\n  And \"Moorg\" should have 12 hitpoints\n```\nThe table is a `Turnip::Table` object which works in much the same way as Cucumber's\n`Cucumber::Ast::Table` objects.\n\nE.g. converting the `Turnip::Table` to an array of hashes:\n\n``` ruby\nstep \"there are the following monsters:\" do |table|\n  @monsters = {}\n  table.hashes.each do |hash|\n    @monsters[hash['Name']] = hash['Hitpoints'].to_i\n  end\nend\n```\n\nor the equivalent:\n\n``` ruby\nstep \"there are the following monsters:\" do |table|\n  @monsters = {}\n  table.rows.each do |(name, hp)|\n    @monsters[name] = hp.to_i\n  end\nend\n```\n\n## Unimplemented steps\nTurnip mark a scenario as pending when steps in the scenario is not implemented.\nIf you sets `raise_error_for_unimplemented_steps` as `true`, turnip will mark a scenario as fail.\n\nIt defaults to `false`, you can change it by adding following configuration to `spec/turnip_helper.rb`:\n\n```ruby\nRSpec.configure do |config|\n  config.raise_error_for_unimplemented_steps = true\nend\n```\n\n## Substitution in Scenario Outlines\n\nYou would be able to use substitution that can be used to DocString and Table arguments in Scenario Outline like [Cucumber](http://cukes.info/step-definitions.html#substitution_in_scenario_outlines):\n\n```cucumber\nScenario Outline: Email confirmation\n  Given I have a user account with my name \"Jojo Binks\"\n  When an Admin grants me \u003cRole\u003e rights\n  Then I should receive an email with the body:\n    \"\"\"\n    Dear Jojo Binks,\n    You have been granted \u003cRole\u003e rights.  You are \u003cdetails\u003e. Please be responsible.\n    -The Admins\n    \"\"\"\n  Examples:\n    |  Role     | details                                         |\n    |  Manager  | now able to manage your employee accounts       |\n    |  Admin    | able to manage any user account on the system   |\n```\n\n## Using with Capybara\n\nJust require `turnip/capybara` in your `spec_helper`. You can now use the same\ntags you'd use in Cucumber to switch between drivers e.g.  `@javascript` or\n`@selenium`. Your Turnip features will also be run with the `:type =\u003e :feature`\nmetadata, so that Capybara is included and also any other extensions you might\nwant to add.\n\n## RSpec custom formatters\n\nTurnip sends notifications to the RSpec reporter about step progress. You can\nlisten for these by registering your formatter class with the following\nnotifications:\n\n``` ruby\nclass MyFormatter\n  RSpec::Core::Formatters.register self, :step_started, :step_passed, :step_failed, :step_pending\n  \n  def step_passed(step)\n    puts \"Starting step: #{step.text}\"\n  end\n\n  # …\nend\n```\n\n## License\n\n(The MIT License)\n\nCopyright (c) 2011-2012 Jonas Nicklas\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the 'Software'), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","funding_links":[],"categories":["Ruby","Testing"],"sub_categories":["Acceptance Test Frameworks"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjnicklas%2Fturnip","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjnicklas%2Fturnip","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjnicklas%2Fturnip/lists"}