Specify

Codeship Status for jnyman/specify

Coverage Status Gem Version Dependency Status Code Climate

Gitter chat endorse Stories in Ready

Specify is a BDD-style micro-framework.

Specify is a very thin wrapper around RSpec that provides a Gherkin-style syntax for use with test examples.

Installation

Add this line to your application's Gemfile:

gem 'specify'

And then execute:

$ bundle

Or install it yourself as:

$ gem install specify

Usage

To use Specify you simply have to require it within your spec_helper file.

While Specify does provide a Gherkin-like syntax, there is no parsing of a Gherkin feature and step definition matching. Just as in RSpec, everything is in one place: the Ruby file. The benefit here is that you don't have to change the text in two places (feature file and step definition file) every time you change something. Further, there are no more matchers to sync up with natural language. Specify uses plain Ruby helper methods coupled with various patterns.

Integration Tests

RSpec's mode of action is that all examples should be completely independent. This is in line with its focus as primarily a unit-based testing framework. For integration purposes, this means if you want to use RSpec you have to write a sequence of examples, each of which repeats the behavior of all previous examples. Another alternative is that you could write one single large example that performs the entire set of actions. The problem in that case is that there is no independent reporting of each step.

This is why tools like Cucumber end up being used.

However the only benefit there is the ability to execute some examples in sequence and skip subsequent steps after a failure.

So that's the goal of Specify: provide just the good parts of Cucumber and skipping all the questionable parts. At minimum this means the ability to chain examples into a series of steps that run in sequence and which stop when a step fails. The idea is to assemble a series of tests that should all pass, but where completely isolating them is not sensible. Hooking this into RSpec, this would make RSpec less unit and more integration.

Example

Consider this example of a standard RSpec example group:

describe 'Basic Spec Structure' do
  context 'when simple logic tests are applied' do
    it 'will agree that true almost certainly not false' do
      expect(true).to_not be false
    end

    it 'will agree that true is pretty definitely true' do
      expect(true).to be true
    end
  end
end

describe 'Simple Scenarios' do
  context 'with an instance variable' do
    it 'will establish a variable' do
      @active = 'testing'
      expect(@active).to eq 'testing'
    end

    it 'will reference the instance variable' do
      puts "@active = #{@active}"
      expect(@active).to eq 'testing'
    end
  end
end

Now consider how this can work with Specify. You will have a "steps" block. Within that, all examples run in order of specification. State is preserved between examples within the "steps" block. If any example inside the "steps" block fails, all remaining steps will be marked as pending and thus skipped.

context 'when simple logic tests are applied' do
  steps do
    Given 'true is almost certainly not false' do
      expect(true).to_not be false
    end

    Given 'true is pretty definitely true' do
      expect(true).to be true
    end
  end
end

context 'Simple Scenarios' do
  steps 'with an instance variable' do
    When 'it establishes an instance variable' do
      @active = 'testing'
      expect(@active).to eq 'testing'
    end

    Then 'that instance variable can be referenced' do
      puts "@active = #{@active}"
      expect(@active).to eq 'testing'
    end
  end
end

Shared Steps

You can use shared_steps to define a block that will be evaluated in the context of an example. The example must use the include_steps directive. Here is an example of some shared steps:

shared_steps 'login' do |email, password|
  When 'I go to login page' do
    puts 'Go to the login page'
  end

  When 'I put credentials' do
    puts "Email: #{email}, Password: #{password}"
  end
end

shared_steps 'invalid login' do
  Then 'I should see login error' do
    puts 'Expect a login error'
  end
end

Here is how those steps can be used:

context 'action' do
  steps 'User provides wrong email' do
    include_steps 'login', 'jeff', 'invalid'
    include_steps 'invalid login'
  end

  steps 'User provides wrong password' do
    include_steps 'login', '[email protected]', 'testing'
    include_steps 'invalid login'
  end
end

Contributing

To work on Specify:

  1. Fork the project.
  2. Create a feature branch. (git checkout -b my-new-feature)
  3. Commit your changes. (git commit -am 'new feature')
  4. Push the branch. (git push origin my-new-feature)
  5. Create a new pull request.

Authors

License

Specify is distributed under the MIT license.