I was about to refactor one of my old ruby apps when I realized that I didn’t want to write more cucumber features and scenarios. I do bdd whenever I can (I even wrote a detailed post about it), but in ruby I don’t want to have test cases both under ./features
for cucumber and ./spec
for rspec any more. I know that it is possible to run both of them at once, but that is not simple: it makes automatic test case execution, getting test coverage, and maintenance complicated. Moreover, it is easy to forget to run one of them before commit, and that usually leads to a failed build. So, I decided that I would ditch cucumber and use only rspec and use it "" do ... end
methods as scenarios and write steps such as when_i_enter_something
, but then I found turnip, and I like it very much.
Moving from cucumber to turnip was pretty easy. It uses the same feature syntax as cucumber, which means that I don’t have to do anything with the feature files besides moving them under the ./spec
folder. I’ve created a acceptance
folder under ./spec
just to keep the acceptance test cases separated from the unit tests.
~/Code/ruby/dina-coding-dojo/numerals % ls -1 features spec/acceptance
features:
numeral_conversion.feature
user_interface.feature
...
spec/acceptance:
numeral_conversion.feature
user_interface.feature
...
And one of the feature files - spec/acceptance/user_interface.feature
:
Feature: Provide a simple form for the user so that she can use the converter
Background:
Given I am on the main page
Scenario: Display a number as a word
Given I am on the main page
When I enter 10 into my field
And I press submit
Then I see it as a name in the result
And I see the original number as well
Scenario: Don't display anything when the user pressed submit without giving a number
Given I am on the main page
When I press submit
Then I do not see any errors just the content of the main page
# ... (more scenarios)
Running the test cases:
~/Code/ruby/dina-coding-dojo/numerals % bundle exec rspec spec
..........
Finished in 3.74 seconds
10 examples, 0 failures
~/Code/ruby/dina-coding-dojo/numerals %
It didn’t work because turnip needs to “know” that it has to run the features, too. It can be easily achieved by adding -r turnip/rspec
to ./.rspec
:
~/Code/ruby/dina-coding-dojo/numerals % cat .rspec
--colour -r turnip/rspec
~/Code/ruby/dina-coding-dojo/numerals %
Now, running the test cases again:
~/Code/ruby/dina-coding-dojo/numerals % bundle exec rspec spec
...
Provide a simple form for the user so that she can use the converter
Don't display anything when the user gave something else than a number
I am on the main page -> I enter something else than a number into my field
-> I press submit -> I do not see any errors just the content of the main page
# No such step: 'I am on the main page'
# ./spec/acceptance/user_interface.feature:68
Finished in 4.18 seconds
50 examples, 0 failures, 40 pending
~/Code/ruby/dina-coding-dojo/numerals %
Nice, that seems to work, and it is time to move the steps, too. They will go under the spec/acceptance/steps
with the name spec/acceptance/steps/user_interface_steps.rb
folder, but will need little change:
# cucumber step:
#...
Given /^I am on the main page$/ do
visit "/"
end
When /^I enter (\d+) into my field$/ do |number|
@number = number
fill_in("number", :with => number)
end
#...
# ---------------------------------------------
# turnip step:
#...
require "spec_helper"
module UserInterfaceSteps
step "I am on the main page" do
visit "/"
end
step "I enter :number into my field" do |number|
@number = number
fill_in("number", :with => number)
end
end
# ...
The body of the method is the same, but the syntax is a bit different. Turnip doesn’t do regexp argumentation (I never understood why that beneficial anyway), it uses symbols as placeholders. As you can see, it is not necessary to put the placeholder between ""
because turnip will find the placeholder without them. Moreover, the steps aren’t global anymore. If you need global ones, just define them in the Turnip::Steps
module.
There are three things that I still have to do: tell turnip where it can load the steps from, make them available for scenarios, and make the capybara
methods - such as visit
- work. Turnip has its own configuration file (./spec/turnip_helper.rb
) that it loads automatically. Add the following line to spec/turnip_helper.rb
, so that the steps are loaded:
Dir.glob("spec/acceptance/steps/**/*_steps.rb") { |f| load f, true }
This will load the steps, and you can use the following line when you want to use them:
# default
RSpec.configure { |c| c.include UserInterfaceSteps }
In this example I’m loading them right after definition:
module UserInterfaceSteps
step "I am on the main page" do
visit "/"
end
# ... (more steps)
end
RSpec.configure { |c| c.include UserInterfaceSteps }
Three more lines in the spec_helper.rb
so that the capybara
methods - such as visit
- can be used in rpsec:
# ...
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rspec'
require 'capybara/rails'
require 'turnip/capybara' # for scopes
# ...
And technically we are done: both the acceptance and unit test cases are executed with one command.
~/Code/ruby/dina-coding-dojo/numerals % bundle exec rspec spec
..................................................
Finished in 5.56 seconds
50 examples, 0 failures
~/Code/ruby/dina-coding-dojo/numerals %
Turnip supports scopes, too (they start with @
like @selenium
or @database
). For example, there are test cases I’d like to run with selenium:
@selenium
Scenario: Display a number as a word
Given I am on the main page
When I enter 10 into my field
And I press submit
Then I see it as a name in the result
And I see the original number as well
This will open my Firefox and run the test case in it, but that will ruin the whole “running everything with one command fast and simple” experience. Fortunately, turnip handles scopes as tags, so it is pretty easy not to run the scoped test cases:
~/Code/ruby/dina-coding-dojo/numerals % bundle exec rspec spec --tag "~selenium"
Run filtered excluding {:selenium=>true}
.................................................
Finished in 4.83 seconds
49 examples, 0 failures
~/Code/ruby/dina-coding-dojo/numerals %
The ~
tells rspec not to run the tests with the specified scope or tag, and the ""
tells my zsh
not to interpret the ~
symbol.
Turnip knows much more then I mentioned in this post (the code samples are here), such as calling steps from other steps, custom steps, placeholders, and different scope options etc., but I won’t write about them now because its README is very detailed. Turnip is a great tool that makes my development work easier and I hope you’ll give it a chance, too.
comments powered by Disqus