Write declarative and vivid cucumber features by referencing shared state across multiple steps.
Add this line to your application's Gemfile:
gem 'fabulist', :git => 'https://github.com/teamaker/Fabulist.git'
And then run:
$ bundle install
In situations where you write conjunctive steps (ie. share state across several step definitions) it's usual to assign instance variables used by the following steps. When doing so, your step definitions have to know implementation details of each other, thus become inflexible and tightly coupled.
Therefore, this gem provides a simple api to memorize arbitrary ruby object and to reference them by:
- class
- any predicate
- insertion order
Let's have a look at this sample scenario:
Feature: Reference shared objects between multiple step definitions
As a developer
I want to show this minmal working example
In order to demonstrate the main pupose of this gem
Scenario: Call by a name
Given I am a user and my name is "John"
When someone asks for John
Then I will respondclass User
attr_accessor :name
def initialize(name)
self.name = name
end
def called(name)
self.name == name
end
endYou can keep track of your domain models with memorize and recall:
Given(/^I am a user and my name is "(.*?)"$/) do |name|
user = User.new(name)
memorize user
end
When(/^someone asks for (.*)$/) do |name|
recall Object, :called, name
endThat's it.
#In this particular case, other ways to call the the user would be:
recall # => user
recall User, :called, "John" # => user
# only this will raise an exception
recall AnotherClass # => raises NoObjectFound
recall User, :called, "Karl" # => raises NoObjectFound
recall User, :whatsoever # => raises NoObjectFound, because the user doesn't respond to 'whatsoever'Your objects lack the ability methods to say whether they have a particular feature or not? If you don't want to bloat your production code with test specific implementation, try to wrap your objects. In the features I use a tagged object to mark arbitrary objects. The tagged object serves as a proxy for the original object. If your objects encapsulate some state but lack the necessary methods, a proxy should check the underlying state of the object.
Are your features first-person narrative? Give the narrator his own representation. Then you can literally interact with the objects that occur in your story.
To make the convenience methods memorize and recall available to your cucumber world, add this line:
# features/support/fabulist.rb
require 'fabulist/cucumber'Do you use ActiveRecord and you always want updated models? You can configure callbacks to define what happens before you memorize an object or recall it from memory.
# features/support/fabulist.rb
require 'fabulist'
Fabulist.configure do |config|
config.before_memorize do |object|
object.save!
end
config.after_recall do |object|
object.reload
end
end➜ Fabulist git:(master) ✗ git rev-parse HEAD
8e528dba9100648c523cd4791bb865703a9fe2ae
➜ Fabulist git:(master) ✗ find lib | grep ".rb$" | xargs cat | wc -l
117- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request