• Stars
    star
    578
  • Rank 74,302 (Top 2 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 12 years ago
  • Updated about 2 months ago

Reviews

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

Repository Details

Spinach is a BDD framework on top of Gherkin.

Spinach - BDD framework on top of Gherkin

Gem Version Tests

Spinach is a high-level BDD framework that leverages the expressive Gherkin language (used by Cucumber) to help you define executable specifications of your application or library's acceptance criteria.

Conceived as an alternative to Cucumber, here are some of its design goals:

  • Step maintainability: since features map to their own classes, their steps are just methods of that class. This encourages step encapsulation.

  • Step reusability: In case you want to reuse steps across features, you can always wrap those in plain ol' Ruby modules.

Spinach is tested against Ruby MRI 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2 as well as **JRuby **.

Getting started

Start by adding spinach to your Gemfile:

group :test do
  gem 'spinach'
  # gem 'rspec'
end

Spinach works out-of-the-box with your favorite test suite, but you can also use it with RSpec as well if you put the following in features/support/env.rb:

require 'rspec'

Now create a features folder in your app or library and write your first feature:

Feature: Test how spinach works
  In order to know what the heck is spinach
  As a developer
  I want it to behave in an expected way

  Scenario: Formal greeting
    Given I have an empty array
    And I append my first name and my last name to it
    When I pass it to my super-duper method
    Then the output should contain a formal greeting

  Scenario: Informal greeting
    Given I have an empty array
    And I append only my first name to it
    When I pass it to my super-duper method
    Then the output should contain a casual greeting

Now for the steps file. Remember that in Spinach steps are just Ruby classes, following a camelcase naming convention. Spinach generator will do some scaffolding for you:

$ spinach --generate

Spinach will detect your features and generate the following class:

features/steps/test_how_spinach_works.rb

class Spinach::Features::TestHowSpinachWorks < Spinach::FeatureSteps
  step 'I have an empty array' do
  end

  step 'I append my first name and my last name to it' do
  end

  step 'I pass it to my super-duper method' do
  end

  step 'the output should contain a formal greeting' do
  end

  step 'I append only my first name to it' do
  end

  step 'the output should contain a casual greeting' do
  end
end

Then, you can fill it in with your logic - remember, it's just a class, you can use private methods, mix in modules or whatever!

class Spinach::Features::TestHowSpinachWorks < Spinach::FeatureSteps
  step 'I have an empty array' do
    @array = Array.new
  end

  step 'I append my first name and my last name to it' do
    @array += ["John", "Doe"]
  end

  step 'I pass it to my super-duper method' do
    @output = capture_output do
      Greeter.greet(@array)
    end
  end

  step 'the output should contain a formal greeting' do
    @output.must_include "Hello, mr. John Doe"
  end

  step 'I append only my first name to it' do
    @array += ["John"]
  end

  step 'the output should contain a casual greeting' do
    @output.must_include "Yo, John! Whassup?"
  end

  private

  def capture_output
    out = StringIO.new
    $stdout = out
    $stderr = out
    yield
    $stdout = STDOUT
    $stderr = STDERR
    out.string
  end
end

module Greeter
  def self.greet(name)
    if name.length > 1
      puts "Hello, mr. #{name.join(' ')}"
    else
      puts "Yo, #{name.first}! Whassup?"
    end
  end
end

Then run your feature again running spinach and watch it all turn green! :)

Shared Steps

You'll often find that some steps need to be used in many features. In this case, it makes sense to put these steps in reusable modules. For example, let's say you need a step that logs the user into the site.

This is one way to make that reusable:

# ... features/steps/common_steps/login.rb
module CommonSteps
  module Login
    include Spinach::DSL

    step 'I am logged in' do
      # log in stuff...
    end
  end
end

Using the module (in any feature):

# ... features/steps/buying_a_widget.rb
class Spinach::Features::BuyAWidget < Spinach::FeatureSteps
  # simply include this module and you are good to go
  include CommonSteps::Login
end

Audit

Over time, the definitions of your features will change. When you add, remove or change steps in the feature files, you can easily audit your existing step files with:

$ spinach --audit

This will find any new steps and print out boilerplate for them, and alert you to the filename and line number of any unused steps in your step files.

This does not modify the step files, so you will need to paste the boilerplate into the appropriate places. If a new feature file is detected, you will be asked to run spinach --generate beforehand.

Important: If auditing individual files, common steps (as above) may be reported as unused when they are actually used in a feature file that is not currently being audited. To avoid this, run the audit with no arguments to audit all step files simultaneously.

Tags

Feature and Scenarios can be marked with tags in the form: @tag. Tags can be used for different purposes:

  • applying some actions using hooks (eg: @javascript, @transaction, @vcr)
# When using Capybara, you can switch the driver to use another one with
# javascript capabilities (Selenium, Poltergeist, capybara-webkit, ...)
#
# Spinach already integrates with Capybara if you add
# `require spinach/capybara` in `features/support/env.rb`.
#
# This example is extracted from this integration.
Spinach.hooks.on_tag("javascript") do
  ::Capybara.current_driver = ::Capybara.javascript_driver
end
  • filtering (eg: @module-a, @customer, @admin, @bug-12, @feat-1)
# Given a feature file with this content

@feat-1
Feature: So something great

  Scenario: Make it possible

  @bug-12
  Scenario: Ensure no regression on this

Then you can run all Scenarios in your suite tagged @feat-1 using:

$ spinach --tags @feat-1

Or only Scenarios tagged either @feat-1 or @bug-12 using:

$ spinach --tags @feat-1,@bug-12

Or only Scenarios tagged @feat-1 that aren't tagged @bug-12 using:

$ spinach --tags @feat-1,~@bug-12

By default Spinach will ignore Scenarios marked with the tag @wip or whose Feature is marked with the tag @wip. Those are meant to be work in progress, scenarios that are pending while you work on them. To explicitly run those, use the --tags option:

$ spinach --tags @wip

Hook architecture

Spinach provides several hooks to allow you performing certain steps before or after any feature, scenario or step execution.

So, for example, you could:

Spinach.hooks.before_scenario do |scenario|
  clear_database
end

Spinach.hooks.on_successful_step do |step, location|
  count_steps(step.scenario.steps)
end

Spinach.hooks.after_run do |status|
  send_mail if status == 0
end

Full hook documentation is here:

Spinach's hook documentation

Local Before and After Hooks

Sometimes it feels awkward to add steps into feature file just because you need to do some test setup and cleanup. And it is equally awkward to add a global hooks for this purpose. For example, if you want to add a session timeout feature, to do so, you want to set the session timeout time to 1 second just for this feature, and put the normal timeout back after this feature. It doesn't make sense to add two steps in the feature file just to change the session timeout value. In this scenario, a before and after blocks are perfect for this kind of tasks. Below is an example implementation:

class Spinach::Features::SessionTimeout < Spinach::FeatureSteps
  attr_accessor :original_timeout_value
  before do
    self.original_timeout_value = session_timeout_value
    change_session_timeout_to 1.second
  end

  after do
    change_session_timeout_to original_timeout_value
  end

  # remaining steps
end

RSpec mocks

If you need access to the rspec-mocks methods in your steps, add this line to your env.rb:

require 'spinach/rspec/mocks'

Reporters

Spinach supports two kinds of reporters by default: stdout and progress. You can specify them when calling the spinach binary:

spinach --reporter progress

When no reporter is specified, stdout will be used by default.

Other reporters:

Wanna use it with Rails 3?

Use spinach-rails

Other rack-based frameworks

Check out our spinach-sinatra demo

Resources

Related gems

Demos

Contributing

You can easily contribute to Spinach. Its codebase is simple and extensively documented.

  • Fork the project.
  • Make your feature addition or bug fix.
  • Add specs for it. This is important so we don't break it in a future version unintentionally.
  • Commit, do not mess with rakefile, version, or history. If you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull.
  • Send me a pull request. Bonus points for topic branches.

License

MIT (Expat) License. Copyright 2011-2023 Codegram Technologies

More Repositories

1

date_validator

A simple, ORM agnostic, Ruby >=2.2 compatible date validator for Rails, based on ActiveModel.
Ruby
493
star
2

pelusa

Static analysis Lint-type tool to improve your OO Ruby code
Ruby
440
star
3

rack-webconsole

Rack-based interactive console (à la Rails console) for your web application's front-end
Ruby
251
star
4

vim-codereview

GItHub Pull Request-based Code Reviews
Ruby
240
star
5

futuroscope

Yet another Futures implementation in Ruby
Ruby
210
star
6

hyperclient

HyperClient is a Ruby Hypermedia API client.
Ruby
152
star
7

resort

Positionless model sorting for Rails
Ruby
103
star
8

spinach-rails

Spinach-rails is a compatibility layer on top of Spinach to provide rails support.
Ruby
62
star
9

vimfiles

Codegram vimfiles
Vim Script
44
star
10

ember-forms

Easy forms for ember.js
JavaScript
37
star
11

gherkin-ruby

gherkin-ruby is a Gherkin parser in pure Ruby using Parslet.
Ruby
33
star
12

markdownizer

Render any text as markdown, with code highlighting and all!
Ruby
30
star
13

colorblind

Extend your ActiveSupport logger with trendy colorschemes from the 90's!
Ruby
27
star
14

live-rust

Rust
27
star
15

haml-jekyll-extension

Jekyll extension to easly use HAML instead of plain HTML
Ruby
21
star
16

jamstack-cfp

Run your call for papers using JAMStack + GitHub Actions
Vue
20
star
17

simple_currency

A really simple currency converter using XavierMedia API. Compatible with Ruby 1.8, 1.9, JRuby 1.5.3 and Rubinius 1.1
Ruby
17
star
18

calbert

Catalan ALBERT (A Lite BERT for self-supervised learning of language representations)
Python
15
star
19

colorant

A small utility to get the colors of an image.
Ruby
15
star
20

bankr

A bank accoount data gathering gem
Ruby
14
star
21

sass-images

Sass extensions to deal with images
Ruby
14
star
22

cheezburger

BDD for lolcats!
Ruby
13
star
23

guard-spinach

Guard-spinach is a guard plugin for spinach
Ruby
13
star
24

dialog-example

Repository with Angular project to show how we use Dialogs and Templates at Empresaula
TypeScript
11
star
25

rpg

RPG game to learn Ruby.
Ruby
9
star
26

dotfiles

Dotfiles used by Codegram team!
Emacs Lisp
8
star
27

spinach-rails-demo

Ruby
8
star
28

vim-todo

Easy peasy TODO list manager for VIM!
Vim Script
8
star
29

acts_as_decimal

Rails 3 plugin to store decimal numbers in the database as integers.
Ruby
7
star
30

pinky

[EXPERIMENTAL] Composable promises for Elixir.
Elixir
7
star
31

useful-github-workflows

Useful GitHub Workflows we use at Codegram
6
star
32

trace

Trace Ruby method calls inside a block of code
Ruby
6
star
33

urania.ex

Efficient and elegant data access for Elixir
Elixir
6
star
34

robinhood

Robin hood leverages celluloid actors to distribute long-lived processes across multiple servers using redis as a locking mechanism.
Ruby
6
star
35

timetable

Timetable is a tool to organize structured content (sections of a workshop, or due tasks), schedule it along time and let you know about current and next sections/tasks.
Ruby
6
star
36

tasca-spinach-demo

Simple todo application as a Spinach demo
Ruby
5
star
37

hypermodel

Rails Responder to generate an automagic JSON HAL representation for your models
Ruby
5
star
38

lastfm

A rather minimal Last.fm client for Ruby.
Ruby
5
star
39

talks

Monday Talks at Codegram
JavaScript
5
star
40

spex

Validate your Elixir values against value-based schemas
Elixir
5
star
41

status-notify-slack

A simple docker image to check if an application status is 200 OK
Shell
3
star
42

hey-mate

A Slack bot to recognize and reward your team's work 🧉
Elixir
3
star
43

vim-haml2slim

A small VIm plugin that helps you convert your Haml views to Slim
Vim Script
3
star
44

phoenix_starter

Elixir
3
star
45

heroku-nginx-proxy

Herouk application that runs a proxy
HTML
3
star
46

dm-machinist

dm-machinist is a datamapper adapter for machinist 2.0
Ruby
3
star
47

course_scraper

A course scraper that gets all the vocational training courses in Catalonia and Spain.
Ruby
2
star
48

full_stack_bot

Full Stack Fest's amazing bot.
Elixir
2
star
49

spinach-sinatra-demo

Spinach working with sinatra
Ruby
2
star
50

deepspain

Deep Spain is a language model fine-tuned on the Spanish state's official bulletin documents
Python
2
star
51

webservice-clj

Example webservice in Clojure for a talk.
Clojure
2
star
52

decidim-barcelona-graphql-api

Simple GraphQL API for decidim.barcelona using Absinthe
Elixir
2
star
53

decidim-deploy-heroku

Opinionated Rails generator for `decidim` apps.
Ruby
2
star
54

decidim-monitor

A tool to monitor instances of decidim.
Elixir
2
star
55

serve-template

Ruby
2
star
56

spinach_helpers

Support helpers to include in Spinach
2
star
57

faye-websockets-heroku-test

Websockets test (a realtime auction app).
JavaScript
2
star
58

codegram.github.io

Codegram Blog
Clojure
2
star
59

gram

Gram is an internal administration tool for Codegram
Ruby
1
star
60

decidim-staging

Demo application to try Codegram's unreleased features of Decidim
HTML
1
star
61

rubocop-decidim

Rubocop custom cops for decidim/decidim
Ruby
1
star
62

spinach-presentation

Showoff presentation for Spinach
1
star
63

button_form

button_form replaces all <input type="submit"/> tags with <button/> in your forms
Ruby
1
star
64

sonic-client

Sonic Client for Elixir
Elixir
1
star
65

serverless-ruby-circleci

A CircleCI orb to build and deploy serverless ruby apps
1
star
66

nuxt-starter

JavaScript
1
star
67

full-stack-fest-sponsor-handbook

Full Stack Fest's sponsor handbook
CSS
1
star
68

wasm-tree

HTML
1
star
69

codeborg

A Hubot for Codegram
CoffeeScript
1
star
70

docker-images

A collection of useful docker images for development
Shell
1
star
71

elixir_starter

Elixir
1
star
72

rails-templates

Ruby
1
star
73

vim-numberlock

Map your numbers to their corresponding Shift+Number symbol for faster typing.
Vim Script
1
star
74

autility

Download utility invoices from some of the popular utility vendors in Spain (Endesa, Vodafone).
Ruby
1
star
75

decidim-electionguard

Python
1
star
76

globalize2_home_page_redirect-radiant-extension

self explanatory
Ruby
1
star
77

awesome-mixins

Awesome compass mixins
1
star
78

hackershout

Shout your hackerness to the world!
Ruby
1
star
79

sass-compass-exercises

Exercises for the sass-compass workshop
Ruby
1
star
80

confs-client

Client for Codegram Conferences
Ruby
1
star