Reputation: 26758
I have a working cucumber setup. My features/
folder has feature and step definitions, and I can call cucumber
at the command line to run the test suite.
I also have a separate script which I would like to enable to call some of the cucumber tests. I know that in a cucumber step definition, it's possible to call a step from another step. But I don't know a way to:
From the relishApp cucumber api docs, I've gathered this:
require 'cucumber'
# run all features
runtime = Cucumber::Runtime.new
Cucumber::Cli::Main.new([]).execute!(runtime)
This will run all my cucumber tests in the exact same way as if I has run cucumber
from the command line, formatting included. However, I'm not sure how use this approach to:
execute!
exits the program).I've also been looking at the cucumber source code to try and write/invoke steps dynamically:
require 'cucumber'
Given(/foo/) {}
# this raises an error:
# NoMethodError: undefined method `register_rb_step_definition'
# for Cucumber::RbSupport::RbLanguage:Class
# from /usr/local/lib/ruby/gems/2.0.0/gems/cucumber/2.4.0/lib
# /cucumber/rb_support/rb_dsl.rb:28
# :in `register_rb_step_definition
# A way to make `'register_rb_step_definition'` succeed:
runtime = Cucumber::Runtime.new
config = Cucumber::Configuration.new
language = Cucumber::RbSupport::RbLanguage.new(runtime, config)
dsl = Cucumber::RbSupport::RbDsl
dsl.rb_language = language
steps = {}
steps["foo"] = dsl.register_rb_step_definiton(/foo (.+)/, ->(args) { puts args })
# Now I have a 'steps' hash like {"foo" => foo_step_object}
# I can call a step like so:
steps["foo"].invoke(["bar"]) # the argument to invoke is an array or hash
# this will print bar to the console like the step proc instructed
This defines and invokes tests successfully, but there are a few shortcomings:
step("foo bar")
instead of writing steps["foo"].invoke(["bar"])
. With my current attempt to invoke steps, the regex in the definitions is ignored.I've been looking at some discussion about Cucumber and it seems there's a push to remove the capability to call a step using the steps
method. In other words, all step nesting should be done through refactoring the code into separate methods, not by referencing other steps. I understand the inspiration for this notion, but I still envision a use-case for:
This is basically how Cucumber works already if it's run with the cucumber
shell command, although it requires all steps to be run within a feature & scenario. If my application only needs 'steps', but needs to define a global 'feature' and 'scenario', so be it, but I'd still like to use Ruby only and not divert to the cucumber
shell command.
Upvotes: 1
Views: 1792
Reputation: 1559
This is an example of what works for me, defining and invoking steps manually:
require 'cucumber' # must require everything; otherwise Configuration cannot be initialized
config = Cucumber::Configuration.new
dsl = Object.new.extend(Cucumber::RbSupport::RbDsl)
rb_language = Cucumber::RbSupport::RbLanguage.new(:unused, config)
step_search = Cucumber::StepMatchSearch.new(rb_language.method(:step_matches), config)
dsl.Given(/hello (.*)/) { |foo| puts "argument given was: #{foo}" }
match = step_search.call('hello world').first
match.step_definition.invoke(match.args) # => argument given was: world
It will raise exceptions for redundant or ambiguous step definitions, too:
dsl.Given(/hello (.*)/) { }
dsl.Given(/(.*) world/) { }
step_search.call('hello world')
# => Cucumber::Ambiguous: Ambiguous match of "hello world"
If you intend to also have actual feature files or step definition files around but don't want them to be included, then have in mind that initialising Configuration
without any parameters will autoload some folders:
config = Cucumber::Configuration.new
config.autoload_code_paths
# => ["features/support", "features/step_definitions"]
config = Cucumber::Configuration.new(autoload_code_paths: [])
config.autoload_code_paths
# => []
Upvotes: 2
Reputation: 4864
What you want is
require 'cucumber'
require 'path_to_step_defs.rb'
What this does is load the file path_to_step_defs.rb
once, and only once. Then you don't need the load_step_definitions
method, because the script file has already loaded what it needs.
Upvotes: 0